diff --git a/.go-version b/.go-version index eb716f77a7b8d..d6c68ad2d09b7 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.24.9 +1.24.11 diff --git a/CHANGELOG/CHANGELOG-1.34.md b/CHANGELOG/CHANGELOG-1.34.md index 336f60a6e28cc..6fa8297f183a7 100644 --- a/CHANGELOG/CHANGELOG-1.34.md +++ b/CHANGELOG/CHANGELOG-1.34.md @@ -1,166 +1,301 @@ -- [v1.34.1](#v1341) - - [Downloads for v1.34.1](#downloads-for-v1341) +- [v1.34.2](#v1342) + - [Downloads for v1.34.2](#downloads-for-v1342) - [Source Code](#source-code) - [Client Binaries](#client-binaries) - [Server Binaries](#server-binaries) - [Node Binaries](#node-binaries) - [Container Images](#container-images) - - [Changelog since v1.34.0](#changelog-since-v1340) + - [Changelog since v1.34.1](#changelog-since-v1341) - [Changes by Kind](#changes-by-kind) + - [Feature](#feature) - [Bug or Regression](#bug-or-regression) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake) - [Dependencies](#dependencies) - [Added](#added) - [Changed](#changed) - [Removed](#removed) -- [v1.34.0](#v1340) - - [Downloads for v1.34.0](#downloads-for-v1340) +- [v1.34.1](#v1341) + - [Downloads for v1.34.1](#downloads-for-v1341) - [Source Code](#source-code-1) - [Client Binaries](#client-binaries-1) - [Server Binaries](#server-binaries-1) - [Node Binaries](#node-binaries-1) - [Container Images](#container-images-1) - - [Changelog since v1.33.0](#changelog-since-v1330) - - [Urgent Upgrade Notes](#urgent-upgrade-notes) - - [(No, really, you MUST read this before you upgrade)](#no-really-you-must-read-this-before-you-upgrade) + - [Changelog since v1.34.0](#changelog-since-v1340) - [Changes by Kind](#changes-by-kind-1) - - [Deprecation](#deprecation) - - [API Change](#api-change) - - [Feature](#feature) - - [Failing Test](#failing-test) - [Bug or Regression](#bug-or-regression-1) - - [Other (Cleanup or Flake)](#other-cleanup-or-flake) - [Dependencies](#dependencies-1) - [Added](#added-1) - [Changed](#changed-1) - [Removed](#removed-1) -- [v1.34.0-rc.2](#v1340-rc2) - - [Downloads for v1.34.0-rc.2](#downloads-for-v1340-rc2) +- [v1.34.0](#v1340) + - [Downloads for v1.34.0](#downloads-for-v1340) - [Source Code](#source-code-2) - [Client Binaries](#client-binaries-2) - [Server Binaries](#server-binaries-2) - [Node Binaries](#node-binaries-2) - [Container Images](#container-images-2) - - [Changelog since v1.34.0-rc.1](#changelog-since-v1340-rc1) + - [Changelog since v1.33.0](#changelog-since-v1330) + - [Urgent Upgrade Notes](#urgent-upgrade-notes) + - [(No, really, you MUST read this before you upgrade)](#no-really-you-must-read-this-before-you-upgrade) - [Changes by Kind](#changes-by-kind-2) + - [Deprecation](#deprecation) + - [API Change](#api-change) - [Feature](#feature-1) - - [Documentation](#documentation) + - [Failing Test](#failing-test) - [Bug or Regression](#bug-or-regression-2) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-1) - [Dependencies](#dependencies-2) - [Added](#added-2) - [Changed](#changed-2) - [Removed](#removed-2) -- [v1.34.0-rc.1](#v1340-rc1) - - [Downloads for v1.34.0-rc.1](#downloads-for-v1340-rc1) +- [v1.34.0-rc.2](#v1340-rc2) + - [Downloads for v1.34.0-rc.2](#downloads-for-v1340-rc2) - [Source Code](#source-code-3) - [Client Binaries](#client-binaries-3) - [Server Binaries](#server-binaries-3) - [Node Binaries](#node-binaries-3) - [Container Images](#container-images-3) - - [Changelog since v1.34.0-rc.0](#changelog-since-v1340-rc0) + - [Changelog since v1.34.0-rc.1](#changelog-since-v1340-rc1) - [Changes by Kind](#changes-by-kind-3) + - [Feature](#feature-2) + - [Documentation](#documentation) - [Bug or Regression](#bug-or-regression-3) - [Dependencies](#dependencies-3) - [Added](#added-3) - [Changed](#changed-3) - [Removed](#removed-3) -- [v1.34.0-rc.0](#v1340-rc0) - - [Downloads for v1.34.0-rc.0](#downloads-for-v1340-rc0) +- [v1.34.0-rc.1](#v1340-rc1) + - [Downloads for v1.34.0-rc.1](#downloads-for-v1340-rc1) - [Source Code](#source-code-4) - [Client Binaries](#client-binaries-4) - [Server Binaries](#server-binaries-4) - [Node Binaries](#node-binaries-4) - [Container Images](#container-images-4) - - [Changelog since v1.34.0-beta.0](#changelog-since-v1340-beta0) + - [Changelog since v1.34.0-rc.0](#changelog-since-v1340-rc0) - [Changes by Kind](#changes-by-kind-4) - - [Deprecation](#deprecation-1) - - [API Change](#api-change-1) - - [Feature](#feature-2) - - [Failing Test](#failing-test-1) - [Bug or Regression](#bug-or-regression-4) - - [Other (Cleanup or Flake)](#other-cleanup-or-flake-1) - [Dependencies](#dependencies-4) - [Added](#added-4) - [Changed](#changed-4) - [Removed](#removed-4) -- [v1.34.0-beta.0](#v1340-beta0) - - [Downloads for v1.34.0-beta.0](#downloads-for-v1340-beta0) +- [v1.34.0-rc.0](#v1340-rc0) + - [Downloads for v1.34.0-rc.0](#downloads-for-v1340-rc0) - [Source Code](#source-code-5) - [Client Binaries](#client-binaries-5) - [Server Binaries](#server-binaries-5) - [Node Binaries](#node-binaries-5) - [Container Images](#container-images-5) - - [Changelog since v1.34.0-alpha.3](#changelog-since-v1340-alpha3) + - [Changelog since v1.34.0-beta.0](#changelog-since-v1340-beta0) - [Changes by Kind](#changes-by-kind-5) - - [API Change](#api-change-2) + - [Deprecation](#deprecation-1) + - [API Change](#api-change-1) - [Feature](#feature-3) + - [Failing Test](#failing-test-1) - [Bug or Regression](#bug-or-regression-5) - [Other (Cleanup or Flake)](#other-cleanup-or-flake-2) - [Dependencies](#dependencies-5) - [Added](#added-5) - [Changed](#changed-5) - [Removed](#removed-5) -- [v1.34.0-alpha.3](#v1340-alpha3) - - [Downloads for v1.34.0-alpha.3](#downloads-for-v1340-alpha3) +- [v1.34.0-beta.0](#v1340-beta0) + - [Downloads for v1.34.0-beta.0](#downloads-for-v1340-beta0) - [Source Code](#source-code-6) - [Client Binaries](#client-binaries-6) - [Server Binaries](#server-binaries-6) - [Node Binaries](#node-binaries-6) - [Container Images](#container-images-6) - - [Changelog since v1.34.0-alpha.2](#changelog-since-v1340-alpha2) + - [Changelog since v1.34.0-alpha.3](#changelog-since-v1340-alpha3) - [Changes by Kind](#changes-by-kind-6) - - [API Change](#api-change-3) + - [API Change](#api-change-2) - [Feature](#feature-4) - - [Failing Test](#failing-test-2) - [Bug or Regression](#bug-or-regression-6) - [Other (Cleanup or Flake)](#other-cleanup-or-flake-3) - [Dependencies](#dependencies-6) - [Added](#added-6) - [Changed](#changed-6) - [Removed](#removed-6) -- [v1.34.0-alpha.2](#v1340-alpha2) - - [Downloads for v1.34.0-alpha.2](#downloads-for-v1340-alpha2) +- [v1.34.0-alpha.3](#v1340-alpha3) + - [Downloads for v1.34.0-alpha.3](#downloads-for-v1340-alpha3) - [Source Code](#source-code-7) - [Client Binaries](#client-binaries-7) - [Server Binaries](#server-binaries-7) - [Node Binaries](#node-binaries-7) - [Container Images](#container-images-7) - - [Changelog since v1.34.0-alpha.1](#changelog-since-v1340-alpha1) + - [Changelog since v1.34.0-alpha.2](#changelog-since-v1340-alpha2) - [Changes by Kind](#changes-by-kind-7) - - [Deprecation](#deprecation-2) - - [API Change](#api-change-4) + - [API Change](#api-change-3) - [Feature](#feature-5) + - [Failing Test](#failing-test-2) - [Bug or Regression](#bug-or-regression-7) - [Other (Cleanup or Flake)](#other-cleanup-or-flake-4) - [Dependencies](#dependencies-7) - [Added](#added-7) - [Changed](#changed-7) - [Removed](#removed-7) -- [v1.34.0-alpha.1](#v1340-alpha1) - - [Downloads for v1.34.0-alpha.1](#downloads-for-v1340-alpha1) +- [v1.34.0-alpha.2](#v1340-alpha2) + - [Downloads for v1.34.0-alpha.2](#downloads-for-v1340-alpha2) - [Source Code](#source-code-8) - [Client Binaries](#client-binaries-8) - [Server Binaries](#server-binaries-8) - [Node Binaries](#node-binaries-8) - [Container Images](#container-images-8) - - [Changelog since v1.33.0](#changelog-since-v1330-1) - - [Urgent Upgrade Notes](#urgent-upgrade-notes-1) - - [(No, really, you MUST read this before you upgrade)](#no-really-you-must-read-this-before-you-upgrade-1) + - [Changelog since v1.34.0-alpha.1](#changelog-since-v1340-alpha1) - [Changes by Kind](#changes-by-kind-8) - - [Deprecation](#deprecation-3) - - [API Change](#api-change-5) + - [Deprecation](#deprecation-2) + - [API Change](#api-change-4) - [Feature](#feature-6) - - [Failing Test](#failing-test-3) - [Bug or Regression](#bug-or-regression-8) - [Other (Cleanup or Flake)](#other-cleanup-or-flake-5) - [Dependencies](#dependencies-8) - [Added](#added-8) - [Changed](#changed-8) - [Removed](#removed-8) +- [v1.34.0-alpha.1](#v1340-alpha1) + - [Downloads for v1.34.0-alpha.1](#downloads-for-v1340-alpha1) + - [Source Code](#source-code-9) + - [Client Binaries](#client-binaries-9) + - [Server Binaries](#server-binaries-9) + - [Node Binaries](#node-binaries-9) + - [Container Images](#container-images-9) + - [Changelog since v1.33.0](#changelog-since-v1330-1) + - [Urgent Upgrade Notes](#urgent-upgrade-notes-1) + - [(No, really, you MUST read this before you upgrade)](#no-really-you-must-read-this-before-you-upgrade-1) + - [Changes by Kind](#changes-by-kind-9) + - [Deprecation](#deprecation-3) + - [API Change](#api-change-5) + - [Feature](#feature-7) + - [Failing Test](#failing-test-3) + - [Bug or Regression](#bug-or-regression-9) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-6) + - [Dependencies](#dependencies-9) + - [Added](#added-9) + - [Changed](#changed-9) + - [Removed](#removed-9) +# v1.34.2 + + +## Downloads for v1.34.2 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes.tar.gz) | 021433b7de611498e31819f53e450fc28c8f9ba83808e2cdf89c235567071f9083cbb7ff8d23ab9aa694ccf252daee71b6a7b01f0e21285b63ad0fcabac4fa16 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-src.tar.gz) | 62d13370dd2c00179c56eb5f20a1d6579c5a378109704b028cf18baeecb2ce8668cdde78ab83da19988d3c5b3c2194526d65619924aaeb7011c5006f889d54e3 + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-darwin-amd64.tar.gz) | 8f907d4af4e70ca04eec7ac37e796ec9d5482b5c01311cb22811c2e4257aef4466ad08df6c629b9e403ae8d9be47887f5777b5e88b9b7ffb5f213f6c1654d783 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-darwin-arm64.tar.gz) | 60dea17783926c611e7007d7c1c8ebb9bae46b18c50400a4ae3a8a1c2c44aed6bf9e4e965e61b97c3d727c261169fde6370477888a4bcfb9be02c3315c880ba9 +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-386.tar.gz) | ed421b143b69eaf6f4f61e78d73af3a613bebd6188708092b175a0f3715a36393aa5c902cb8023f46e5c0d67c2d36a3e1c08a003d46977dcd4a32631b7002d57 +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-amd64.tar.gz) | a9656e446054151390279b5d2a57cdc52cc0546ae5dcac17c370cc4435d93486da9d9603f560cfca64233ad2f9642dd41219c2f308196491af194faefe2cf2ae +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-arm.tar.gz) | cb83ed2eea829d1fc5bbd8f0acfeb40a3db544298f1eb47ed4a1e2ed54880c213eef65dae5d2c25948a1e557a1061cbc2512d9ea7284f6e0a58e167455ca04c2 +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-arm64.tar.gz) | ffbe3367b61531db26494388776f53fe9c83793dc7bab83c3435e5d884846ac25f8e9b8132be083bcdc120462d00ea108dcc033790ff50be20ccb396de6ab786 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-ppc64le.tar.gz) | d19b03f051ea3549f117ab40042ff8817f53cf50ae7c6a32300529fac5beb7c06890f5ca57c0f1785b2dfdd2e9d67a78409bb90e5e0bd2465896ab0ff63442ac +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-linux-s390x.tar.gz) | ac43a42c63b299b3e56dab1ef4cd8e3a6b88fa359609634776cd596833cd64e3a1ec64e09df73b7d8424979c2cb5d477db490640abaf5bf9caca991fcaae1bc6 +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-windows-386.tar.gz) | caa45ee27bffc1b7e8aca0c76f794d9d4cfc33da58182134dcd39e2a4fa6204c5c6ebbbd6f301fd25cc0048ac6e33c6c4618e2a988311d8a7b949a2a57a79f33 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-windows-amd64.tar.gz) | 15721127d8956011a3e5615ca8e29313b6c790ef1f0b914b1603787062c90614bec1d986d8b84930a3ad51b849312a87051cf29ce7c10bf9836c6ac1a07aaebc +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-client-windows-arm64.tar.gz) | 42fc0311183f236e161e42be790305454295dd60aac21ce16e8487814de8b999cbee479bea367b093b15f1a76c260b0f62c792fc302a725fbcd39bd968fe4c38 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-server-linux-amd64.tar.gz) | 016b09cb70b4335201095b96cd564059eece9ece9e945e045b4558afa9dbd99840cd7a48ed6c06a888276c939c8dc30afc8a7435689c9efd295a9f9de1f477c3 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-server-linux-arm64.tar.gz) | b5a74b68e3e88f420ff6bae262f0611a80360672a05170b71613babb4fd072f36b2f067347feb16f0a9c1b20bca01fc8c07d83e3a55ecd133615c9fa6e5066ca +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-server-linux-ppc64le.tar.gz) | bc4e7f1986a802cd35e725854daa18dd68528629d31751e7b62ec292d17cc98ce883e369d05cf55063964a27f5482e548cd22879d9e8954f41a81c68b3452bdd +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-server-linux-s390x.tar.gz) | 2df1a9e454b47cecf13cf79197d426178dd0a57f2a05b1e28223ea0b4fc7cadd7016a751e1f02ac08f286b0b5aec6f8ea7ca086829ab86131a21c01223a586f7 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-node-linux-amd64.tar.gz) | 7a0e3fa6a6afd29e7191b4fabd1cb994458804c8a5cf1b622da77bcd6773244d23bf7f9e0e4b0b6f1485f33dd6d8908ea7e1faad4ba6c02d9c2fba3ce99ba888 +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-node-linux-arm64.tar.gz) | 71f2b692631c8cb541eb9e8950cafb4c28e2b7fc0ee70ded18ca904cc5c8d56e47cbe6d5a4ebac5c41d6386e1b709da213b6eb9e1a56a608e635fda2aa1fa796 +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-node-linux-ppc64le.tar.gz) | 87dc3249735fd66bf07f7249dc84c184895f696ac1954ec99d41ef80329c0feec9120966e98f26c99402a1417ef738334121e515c6153b5d28862eecff026f00 +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-node-linux-s390x.tar.gz) | 6817f55f9aba80221d61043d3a8374ecf52777b94da7f6e3edad5f23813ee9a5117e26c94b020176fe667b3d295a06914ccb1d5be8f5bc527fe39e84ab8e93e7 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.2/kubernetes-node-windows-amd64.tar.gz) | 82041b0cc8652b232aff3ead79c8e19260ecc19d7164f852a4982f0ae70c2a7ac8d4ae0c3f8f01f1825ac1645e4ed9049f908a1931522a3f7b16dc456c54f42a + +### Container Images + +All container images are available as manifest lists and support the described +architectures. It is also possible to pull a specific architecture directly by +adding the "-$ARCH" suffix to the container image name. + +name | architectures +---- | ------------- +[registry.k8s.io/conformance:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/conformance) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/conformance-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/conformance-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/conformance-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/conformance-s390x) +[registry.k8s.io/kube-apiserver:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-apiserver) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-apiserver-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-apiserver-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-apiserver-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-apiserver-s390x) +[registry.k8s.io/kube-controller-manager:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-controller-manager) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-controller-manager-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-controller-manager-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-controller-manager-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-controller-manager-s390x) +[registry.k8s.io/kube-proxy:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-proxy) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-proxy-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-proxy-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-proxy-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-proxy-s390x) +[registry.k8s.io/kube-scheduler:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-scheduler) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-scheduler-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-scheduler-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-scheduler-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kube-scheduler-s390x) +[registry.k8s.io/kubectl:v1.34.2](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kubectl) | [amd64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kubectl-amd64), [arm64](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kubectl-arm64), [ppc64le](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kubectl-ppc64le), [s390x](https://console.cloud.google.com/artifacts/docker/k8s-artifacts-prod/southamerica-east1/images/kubectl-s390x) + +## Changelog since v1.34.1 + +## Changes by Kind + +### Feature + +- Kubernetes is now built using Go 1.24.7 ([#134222](https://github.com/kubernetes/kubernetes/pull/134222), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] +- Kubernetes is now built using Go 1.24.9 + - update setcap and debian-base to bookworm-v1.0.6 ([#134612](https://github.com/kubernetes/kubernetes/pull/134612), [@cpanato](https://github.com/cpanato)) [SIG Architecture, Cloud Provider, Etcd, Release, Storage and Testing] + +### Bug or Regression + +- Bump system-validators to v1.10.2: remove version-specific cgroup kernel config checks to avoid false failures on cgroup v2 systems when v1-only configs are missing. ([#134087](https://github.com/kubernetes/kubernetes/pull/134087), [@pacoxu](https://github.com/pacoxu)) [SIG Cluster Lifecycle] +- Fix Windows kube-proxy (winkernel) issue where stale RemoteEndpoints remained + when a Deployment was referenced by multiple Services due to premature clearing + of the terminatedEndpoints map. ([#135170](https://github.com/kubernetes/kubernetes/pull/135170), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] +- Fix Windows kube-proxy to prevent intermittent deletion of ClusterIP load balancers in HNS when internalTrafficPolicy=Local, ensuring stable service connectivity. ([#134031](https://github.com/kubernetes/kubernetes/pull/134031), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] +- Fix missing kubelet_volume_stats_* metrics ([#133905](https://github.com/kubernetes/kubernetes/pull/133905), [@huww98](https://github.com/huww98)) [SIG Instrumentation and Node] +- Fix panic on kubectl api-resources ([#134912](https://github.com/kubernetes/kubernetes/pull/134912), [@rikatz](https://github.com/rikatz)) [SIG CLI] +- Fix the bug which could result in Job status updates failing with the error: + status.startTime: Required value: startTime cannot be removed for unsuspended job + The error could be raised after a Job is resumed, if started and suspended previously. ([#135130](https://github.com/kubernetes/kubernetes/pull/135130), [@dejanzele](https://github.com/dejanzele)) [SIG Apps and Testing] +- Fix: The requests for a config FromClass in the status of a ResourceClaim were not referenced. ([#135098](https://github.com/kubernetes/kubernetes/pull/135098), [@LionelJouin](https://github.com/LionelJouin)) [SIG Node] +- Fixed a bug in kube-proxy nftables mode (GA as of 1.33) that fails to determine if traffic originates from a local source on the node. The issue was caused by using the wrong meta `iif` instead of `iifname` for name based matches. ([#134118](https://github.com/kubernetes/kubernetes/pull/134118), [@jack4it](https://github.com/jack4it)) [SIG Network] +- Fixed a bug in kube-scheduler where pending pod preemption caused preemptor pods to be retried more frequently. ([#134247](https://github.com/kubernetes/kubernetes/pull/134247), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] +- Fixed a startup probe race condition that caused main containers to remain stuck in "Initializing" state when sidecar containers with startup probes failed initially but succeeded on restart in pods with restartPolicy=Never. ([#134800](https://github.com/kubernetes/kubernetes/pull/134800), [@yuanwang04](https://github.com/yuanwang04)) [SIG Node and Testing] +- Kube-controller-manager: Fixes a 1.34 regression, which triggered a spurious rollout of existing statefulsets when upgrading the control plane from 1.33 → 1.34. This fix is guarded by a `StatefulSetSemanticRevisionComparison` feature gate, which is enabled by default. ([#135087](https://github.com/kubernetes/kubernetes/pull/135087), [@liggitt](https://github.com/liggitt)) [SIG Apps] +- Kube-controller-manager: Resolves potential issues handling pods with incorrect uids in their ownerReference ([#134658](https://github.com/kubernetes/kubernetes/pull/134658), [@liggitt](https://github.com/liggitt)) [SIG Apps] +- Kube-scheduler: Pod statuses no longer include specific taint keys or values when scheduling fails because of untolerated taints ([#135023](https://github.com/kubernetes/kubernetes/pull/135023), [@hoskeri](https://github.com/hoskeri)) [SIG Scheduling] +- Kubeadm: avoid panicing if the user has malformed the kubeconfig in the cluster-info config map to not include a valid current context. Include proper validation at the appropriate locations and throw errors instead. ([#134723](https://github.com/kubernetes/kubernetes/pull/134723), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: ensured waiting for apiserver uses a local client that doesn't reach to the control plane endpoint and instead reaches directly to the local API server endpoint. ([#134270](https://github.com/kubernetes/kubernetes/pull/134270), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: fixed a bug where the node registration information for a given node was not fetched correctly during "kubeadm upgrade node" and the node name can end up being incorrect in cases where the node name is not the same as the host name. ([#134362](https://github.com/kubernetes/kubernetes/pull/134362), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: fixes a preflight check that can fail hostname construction in IPV6 setups ([#134589](https://github.com/kubernetes/kubernetes/pull/134589), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth, Cloud Provider, Cluster Lifecycle and Testing] +- Kubelet: the connection to a DRA driver became unusable because of an internal deadlock when a connection was idle for 30 minutes. ([#133934](https://github.com/kubernetes/kubernetes/pull/133934), [@pohly](https://github.com/pohly)) [SIG Node] +- Reduce event spam during volume operation errors in Portworx in-tree driver ([#135191](https://github.com/kubernetes/kubernetes/pull/135191), [@gohilankit](https://github.com/gohilankit)) [SIG Storage] +- Remove incorrectly printed warning for SessionAffinity whenever a headless service is creater or updated ([#134133](https://github.com/kubernetes/kubernetes/pull/134133), [@Peac36](https://github.com/Peac36)) [SIG Network] +- The SchedulerAsyncAPICalls feature gate has been disabled to mitigate a bug where its interaction with asynchronous preemption in could degrade kube-scheduler performance, particularly under high kube-apiserver load. ([#134401](https://github.com/kubernetes/kubernetes/pull/134401), [@macsko](https://github.com/macsko)) [SIG Scheduling] + +### Other (Cleanup or Flake) + +- Kubeadm: updated the supported etcd version to v3.5.24 for the skewed control plane version v1.33. ([#134861](https://github.com/kubernetes/kubernetes/pull/134861), [@joshjms](https://github.com/joshjms)) [SIG Cluster Lifecycle] + +## Dependencies + +### Added +_Nothing has changed._ + +### Changed +- k8s.io/system-validators: v1.10.1 → v1.10.2 + +### Removed +_Nothing has changed._ + + + # v1.34.1 diff --git a/build/build-image/cross/VERSION b/build/build-image/cross/VERSION index 14a4b84c2030d..411708bd22a9a 100644 --- a/build/build-image/cross/VERSION +++ b/build/build-image/cross/VERSION @@ -1 +1 @@ -v1.34.0-go1.24.9-bullseye.0 +v1.34.0-go1.24.11-bullseye.0 diff --git a/build/common.sh b/build/common.sh index 2bd3cd052272c..c94fede839588 100755 --- a/build/common.sh +++ b/build/common.sh @@ -97,8 +97,8 @@ readonly KUBE_RSYNC_PORT="${KUBE_RSYNC_PORT:-}" readonly KUBE_CONTAINER_RSYNC_PORT=8730 # These are the default versions (image tags) for their respective base images. -readonly __default_distroless_iptables_version=v0.7.11 -readonly __default_go_runner_version=v2.4.0-go1.24.9-bookworm.0 +readonly __default_distroless_iptables_version=v0.7.13 +readonly __default_go_runner_version=v2.4.0-go1.24.11-bookworm.0 readonly __default_setcap_version=bookworm-v1.0.6 # These are the base images for the Docker-wrapped binaries. @@ -621,7 +621,7 @@ function kube::build::start_rsyncd_container() { fi local container_ip - container_ip=$("${DOCKER[@]}" inspect --format '{{ .NetworkSettings.IPAddress }}' "${KUBE_RSYNC_CONTAINER_NAME}") + container_ip=$("${DOCKER[@]}" inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}},{{end}}' "${KUBE_RSYNC_CONTAINER_NAME}" | cut -d',' -f1) # Sometimes we can reach rsync through localhost and a NAT'd port. Other # times (when we are running in another docker container on the Jenkins diff --git a/build/dependencies.yaml b/build/dependencies.yaml index 3abc723bea0f9..18951babab16c 100644 --- a/build/dependencies.yaml +++ b/build/dependencies.yaml @@ -113,7 +113,7 @@ dependencies: # Golang # TODO: this should really be eliminated and controlled by .go-version - name: "golang: upstream version" - version: 1.24.9 + version: 1.24.11 refPaths: - path: .go-version - path: build/build-image/cross/VERSION @@ -134,7 +134,7 @@ dependencies: match: minimum_go_version=go([0-9]+\.[0-9]+) - name: "registry.k8s.io/kube-cross: dependents" - version: v1.34.0-go1.24.9-bullseye.0 + version: v1.34.0-go1.24.11-bullseye.0 refPaths: - path: build/build-image/cross/VERSION @@ -170,7 +170,7 @@ dependencies: match: registry\.k8s\.io\/build-image\/debian-base:[a-zA-Z]+\-v((([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?) - name: "registry.k8s.io/distroless-iptables: dependents" - version: v0.7.11 + version: v0.7.13 refPaths: - path: build/common.sh match: __default_distroless_iptables_version= @@ -178,7 +178,7 @@ dependencies: match: configs\[DistrolessIptables\] = Config{list\.BuildImageRegistry, "distroless-iptables", "v([0-9]+)\.([0-9]+)\.([0-9]+)"} - name: "registry.k8s.io/go-runner: dependents" - version: v2.4.0-go1.24.9-bookworm.0 + version: v2.4.0-go1.24.11-bookworm.0 refPaths: - path: build/common.sh match: __default_go_runner_version= diff --git a/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go b/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go index 860175e8adda1..f7f6c893f7ee8 100644 --- a/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go +++ b/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go @@ -88,9 +88,6 @@ func NewEtcdJoinPhase() workflow.Phase { Example: etcdJoinExample, InheritFlags: getControlPlaneJoinPhaseFlags("etcd"), ArgsValidator: cobra.NoArgs, - // TODO: unhide this phase once ControlPlaneKubeletLocalMode goes GA: - // https://github.com/kubernetes/enhancements/issues/4471 - Hidden: true, // Only run this phase as if `ControlPlaneKubeletLocalMode` is activated. RunIf: func(c workflow.RunData) (bool, error) { return checkFeatureState(c, features.ControlPlaneKubeletLocalMode, true) diff --git a/openshift-hack/images/hyperkube/Dockerfile.rhel b/openshift-hack/images/hyperkube/Dockerfile.rhel index 00c494e974214..2b364e83f2636 100644 --- a/openshift-hack/images/hyperkube/Dockerfile.rhel +++ b/openshift-hack/images/hyperkube/Dockerfile.rhel @@ -14,4 +14,4 @@ COPY --from=builder /tmp/build/* /usr/bin/ LABEL io.k8s.display-name="OpenShift Kubernetes Server Commands" \ io.k8s.description="OpenShift is a platform for developing, building, and deploying containerized applications." \ io.openshift.tags="openshift,hyperkube" \ - io.openshift.build.versions="kubernetes=1.34.2" \ No newline at end of file + io.openshift.build.versions="kubernetes=1.34.3" \ No newline at end of file diff --git a/pkg/kubelet/cm/devicemanager/plugin/v1beta1/server.go b/pkg/kubelet/cm/devicemanager/plugin/v1beta1/server.go index 56a379553e144..bfb5865d38f9a 100644 --- a/pkg/kubelet/cm/devicemanager/plugin/v1beta1/server.go +++ b/pkg/kubelet/cm/devicemanager/plugin/v1beta1/server.go @@ -57,8 +57,8 @@ type server struct { chandler ClientHandler clients map[string]Client - // isStarted indicates whether the service has started successfully. - isStarted bool + // lastError records the last runtime error. A server is considered healthy till an actual error occurs. + lastError error api.UnsafeRegistrationServer } @@ -119,7 +119,7 @@ func (s *server) Start() error { defer s.wg.Done() s.setHealthy() if err = s.grpc.Serve(ln); err != nil { - s.setUnhealthy() + s.setUnhealthy(err) klog.ErrorS(err, "Error while serving device plugin registration grpc server") } }() @@ -210,18 +210,19 @@ func (s *server) Name() string { } func (s *server) Check(_ *http.Request) error { - if s.isStarted { - return nil - } - return fmt.Errorf("device plugin registration gRPC server failed and no device plugins can register") + return s.lastError } // setHealthy sets the health status of the gRPC server. func (s *server) setHealthy() { - s.isStarted = true + s.lastError = nil } // setUnhealthy sets the health status of the gRPC server to unhealthy. -func (s *server) setUnhealthy() { - s.isStarted = false +func (s *server) setUnhealthy(err error) { + if err == nil { + s.lastError = fmt.Errorf("device registration error: device plugin registration gRPC server failed and no device plugins can register") + return + } + s.lastError = fmt.Errorf("device registration error: device plugin registration gRPC server failed and no device plugins can register: %w", err) } diff --git a/pkg/volume/csi/csi_block.go b/pkg/volume/csi/csi_block.go index 4626751c393c1..5613b354c0cd3 100644 --- a/pkg/volume/csi/csi_block.go +++ b/pkg/volume/csi/csi_block.go @@ -68,7 +68,6 @@ package csi import ( "context" "errors" - "fmt" "os" "path/filepath" @@ -171,8 +170,8 @@ func (m *csiBlockMapper) stageVolumeForBlock( if csiSource.NodeStageSecretRef != nil { nodeStageSecrets, err = getCredentialsFromSecret(m.k8s, csiSource.NodeStageSecretRef) if err != nil { - return "", fmt.Errorf("failed to get NodeStageSecretRef %s/%s: %v", - csiSource.NodeStageSecretRef.Namespace, csiSource.NodeStageSecretRef.Name, err) + return "", volumetypes.NewTransientOperationFailure(log("failed to get NodeStageSecretRef %s/%s: %v", + csiSource.NodeStageSecretRef.Namespace, csiSource.NodeStageSecretRef.Name, err)) } } @@ -223,11 +222,11 @@ func (m *csiBlockMapper) publishVolumeForBlock( volAttribs := csiSource.VolumeAttributes podInfoEnabled, err := m.plugin.podInfoEnabled(string(m.driverName)) if err != nil { - return "", errors.New(log("blockMapper.publishVolumeForBlock failed to assemble volume attributes: %v", err)) + return "", volumetypes.NewTransientOperationFailure(log("blockMapper.publishVolumeForBlock failed to assemble volume attributes: %v", err)) } volumeLifecycleMode, err := m.plugin.getVolumeLifecycleMode(m.spec) if err != nil { - return "", errors.New(log("blockMapper.publishVolumeForBlock failed to get VolumeLifecycleMode: %v", err)) + return "", volumetypes.NewTransientOperationFailure(log("blockMapper.publishVolumeForBlock failed to get VolumeLifecycleMode: %v", err)) } if podInfoEnabled { volAttribs = mergeMap(volAttribs, getPodInfoAttrs(m.pod, volumeLifecycleMode)) @@ -237,7 +236,7 @@ func (m *csiBlockMapper) publishVolumeForBlock( if csiSource.NodePublishSecretRef != nil { nodePublishSecrets, err = getCredentialsFromSecret(m.k8s, csiSource.NodePublishSecretRef) if err != nil { - return "", errors.New(log("blockMapper.publishVolumeForBlock failed to get NodePublishSecretRef %s/%s: %v", + return "", volumetypes.NewTransientOperationFailure(log("blockMapper.publishVolumeForBlock failed to get NodePublishSecretRef %s/%s: %v", csiSource.NodePublishSecretRef.Namespace, csiSource.NodePublishSecretRef.Name, err)) } } @@ -304,7 +303,7 @@ func (m *csiBlockMapper) SetUpDevice() (string, error) { attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName) attachment, err = m.k8s.StorageV1().VolumeAttachments().Get(context.TODO(), attachID, meta.GetOptions{}) if err != nil { - return "", errors.New(log("blockMapper.SetupDevice failed to get volume attachment [id=%v]: %v", attachID, err)) + return "", volumetypes.NewTransientOperationFailure(log("blockMapper.SetupDevice failed to get volume attachment [id=%v]: %v", attachID, err)) } } @@ -366,7 +365,7 @@ func (m *csiBlockMapper) MapPodDevice() (string, error) { attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName) attachment, err = m.k8s.StorageV1().VolumeAttachments().Get(context.TODO(), attachID, meta.GetOptions{}) if err != nil { - return "", errors.New(log("blockMapper.MapPodDevice failed to get volume attachment [id=%v]: %v", attachID, err)) + return "", volumetypes.NewTransientOperationFailure(log("blockMapper.MapPodDevice failed to get volume attachment [id=%v]: %v", attachID, err)) } } diff --git a/pkg/volume/csi/csi_block_test.go b/pkg/volume/csi/csi_block_test.go index 3b06ff1c7c771..deffc6b39f902 100644 --- a/pkg/volume/csi/csi_block_test.go +++ b/pkg/volume/csi/csi_block_test.go @@ -18,6 +18,7 @@ package csi import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -491,6 +492,46 @@ func TestBlockMapperMapPodDeviceNoClientError(t *testing.T) { } } +func TestBlockMapperMapPodDeviceGetStageSecretsError(t *testing.T) { + transientError := volumetypes.NewTransientOperationFailure("") + plug, tmpDir := newTestPlugin(t, nil) + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Error(err) + } + }() + + csiMapper, _, pv, err := prepareBlockMapperTest(plug, "test-pv", t) + if err != nil { + t.Fatalf("Failed to make a new Mapper: %v", err) + } + + // set a stage secret for the pv + pv.Spec.PersistentVolumeSource.CSI.NodePublishSecretRef = &api.SecretReference{ + Name: "foo", + Namespace: "default", + } + pvName := pv.GetName() + nodeName := string(plug.host.GetNodeName()) + + csiMapper.csiClient = setupClient(t, true) + + attachID := getAttachmentName(csiMapper.volumeID, string(csiMapper.driverName), nodeName) + attachment := makeTestAttachment(attachID, nodeName, pvName) + attachment.Status.Attached = true + if _, err = csiMapper.k8s.StorageV1().VolumeAttachments().Create(context.Background(), attachment, metav1.CreateOptions{}); err != nil { + t.Fatalf("failed to setup VolumeAttachment: %v", err) + } + t.Log("created attachment ", attachID) + + _, err = csiMapper.MapPodDevice() + if err == nil { + t.Errorf("test should fail, but no error occurred") + } else if !errors.As(err, &transientError) { + t.Errorf("expected a transient error but got %v", err) + } +} + func TestBlockMapperTearDownDevice(t *testing.T) { plug, tmpDir := newTestPlugin(t, nil) defer os.RemoveAll(tmpDir) diff --git a/staging/publishing/rules.yaml b/staging/publishing/rules.yaml index 76723a7c5d3a2..78ed1f87c81f4 100644 --- a/staging/publishing/rules.yaml +++ b/staging/publishing/rules.yaml @@ -19,13 +19,13 @@ rules: dirs: - staging/src/k8s.io/apimachinery - name: release-1.33 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.33 dirs: - staging/src/k8s.io/apimachinery - name: release-1.34 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.34 dirs: @@ -60,7 +60,7 @@ rules: dirs: - staging/src/k8s.io/api - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -69,7 +69,7 @@ rules: dirs: - staging/src/k8s.io/api - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -125,7 +125,7 @@ rules: go build -mod=mod ./... go test -mod=mod ./... - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -140,7 +140,7 @@ rules: go build -mod=mod ./... go test -mod=mod ./... - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -184,7 +184,7 @@ rules: dirs: - staging/src/k8s.io/code-generator - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -193,7 +193,7 @@ rules: dirs: - staging/src/k8s.io/code-generator - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -242,7 +242,7 @@ rules: dirs: - staging/src/k8s.io/component-base - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -255,7 +255,7 @@ rules: dirs: - staging/src/k8s.io/component-base - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -309,7 +309,7 @@ rules: dirs: - staging/src/k8s.io/component-helpers - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -322,7 +322,7 @@ rules: dirs: - staging/src/k8s.io/component-helpers - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -364,7 +364,7 @@ rules: dirs: - staging/src/k8s.io/kms - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -373,7 +373,7 @@ rules: dirs: - staging/src/k8s.io/kms - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -435,7 +435,7 @@ rules: dirs: - staging/src/k8s.io/apiserver - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -452,7 +452,7 @@ rules: dirs: - staging/src/k8s.io/apiserver - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -534,7 +534,7 @@ rules: dirs: - staging/src/k8s.io/kube-aggregator - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -555,7 +555,7 @@ rules: dirs: - staging/src/k8s.io/kube-aggregator - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -655,7 +655,7 @@ rules: # assumes GO111MODULE=on go build -mod=mod . - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -681,7 +681,7 @@ rules: # assumes GO111MODULE=on go build -mod=mod . - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -768,7 +768,7 @@ rules: # assumes GO111MODULE=on go build -mod=mod . - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -788,7 +788,7 @@ rules: # assumes GO111MODULE=on go build -mod=mod . - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -878,7 +878,7 @@ rules: required-packages: - k8s.io/code-generator - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -901,7 +901,7 @@ rules: required-packages: - k8s.io/code-generator - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -970,7 +970,7 @@ rules: dirs: - staging/src/k8s.io/metrics - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -985,7 +985,7 @@ rules: dirs: - staging/src/k8s.io/metrics - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1041,7 +1041,7 @@ rules: dirs: - staging/src/k8s.io/cli-runtime - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1054,7 +1054,7 @@ rules: dirs: - staging/src/k8s.io/cli-runtime - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1114,7 +1114,7 @@ rules: dirs: - staging/src/k8s.io/sample-cli-plugin - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1129,7 +1129,7 @@ rules: dirs: - staging/src/k8s.io/sample-cli-plugin - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1190,7 +1190,7 @@ rules: dirs: - staging/src/k8s.io/kube-proxy - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -1205,7 +1205,7 @@ rules: dirs: - staging/src/k8s.io/kube-proxy - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1240,13 +1240,13 @@ rules: dirs: - staging/src/k8s.io/cri-api - name: release-1.33 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.33 dirs: - staging/src/k8s.io/cri-api - name: release-1.34 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.34 dirs: @@ -1305,7 +1305,7 @@ rules: dirs: - staging/src/k8s.io/cri-client - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1322,7 +1322,7 @@ rules: dirs: - staging/src/k8s.io/cri-client - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1404,7 +1404,7 @@ rules: dirs: - staging/src/k8s.io/kubelet - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -1425,7 +1425,7 @@ rules: dirs: - staging/src/k8s.io/kubelet - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1493,7 +1493,7 @@ rules: dirs: - staging/src/k8s.io/kube-scheduler - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -1508,7 +1508,7 @@ rules: dirs: - staging/src/k8s.io/kube-scheduler - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1582,7 +1582,7 @@ rules: dirs: - staging/src/k8s.io/controller-manager - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1601,7 +1601,7 @@ rules: dirs: - staging/src/k8s.io/controller-manager - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1691,7 +1691,7 @@ rules: dirs: - staging/src/k8s.io/cloud-provider - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1714,7 +1714,7 @@ rules: dirs: - staging/src/k8s.io/cloud-provider - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1814,7 +1814,7 @@ rules: dirs: - staging/src/k8s.io/kube-controller-manager - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -1839,7 +1839,7 @@ rules: dirs: - staging/src/k8s.io/kube-controller-manager - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1899,7 +1899,7 @@ rules: dirs: - staging/src/k8s.io/cluster-bootstrap - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -1910,7 +1910,7 @@ rules: dirs: - staging/src/k8s.io/cluster-bootstrap - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -1956,7 +1956,7 @@ rules: dirs: - staging/src/k8s.io/csi-translation-lib - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -1967,7 +1967,7 @@ rules: dirs: - staging/src/k8s.io/csi-translation-lib - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -1998,13 +1998,13 @@ rules: dirs: - staging/src/k8s.io/mount-utils - name: release-1.33 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.33 dirs: - staging/src/k8s.io/mount-utils - name: release-1.34 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.34 dirs: @@ -2081,7 +2081,7 @@ rules: dirs: - staging/src/k8s.io/kubectl - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -2104,7 +2104,7 @@ rules: dirs: - staging/src/k8s.io/kubectl - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -2186,7 +2186,7 @@ rules: dirs: - staging/src/k8s.io/pod-security-admission - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -2205,7 +2205,7 @@ rules: dirs: - staging/src/k8s.io/pod-security-admission - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -2301,7 +2301,7 @@ rules: dirs: - staging/src/k8s.io/dynamic-resource-allocation - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.33 @@ -2326,7 +2326,7 @@ rules: dirs: - staging/src/k8s.io/dynamic-resource-allocation - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: apimachinery branch: release-1.34 @@ -2397,7 +2397,7 @@ rules: dirs: - staging/src/k8s.io/endpointslice - name: release-1.33 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.33 @@ -2412,7 +2412,7 @@ rules: dirs: - staging/src/k8s.io/endpointslice - name: release-1.34 - go: 1.24.9 + go: 1.24.11 dependencies: - repository: api branch: release-1.34 @@ -2440,17 +2440,17 @@ rules: dirs: - staging/src/k8s.io/externaljwt - name: release-1.33 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.33 dirs: - staging/src/k8s.io/externaljwt - name: release-1.34 - go: 1.24.9 + go: 1.24.11 source: branch: release-1.34 dirs: - staging/src/k8s.io/externaljwt recursive-delete-patterns: - '*/.gitattributes' -default-go-version: 1.24.9 +default-go-version: 1.24.11 diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go index d243b0710bc95..08ddcbf0a0a32 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/generic/policy_matcher.go @@ -17,6 +17,7 @@ limitations under the License. package generic import ( + "context" "fmt" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -41,8 +42,8 @@ type PolicyMatcher interface { BindingMatches(a admission.Attributes, o admission.ObjectInterfaces, binding BindingAccessor) (bool, error) // GetNamespace retrieves the Namespace resource by the given name. The name may be empty, in which case - // GetNamespace must return nil, nil - GetNamespace(name string) (*corev1.Namespace, error) + // GetNamespace must return nil, NotFound + GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) } type matcher struct { @@ -82,8 +83,8 @@ func (c *matcher) BindingMatches(a admission.Attributes, o admission.ObjectInter return isMatch, err } -func (c *matcher) GetNamespace(name string) (*corev1.Namespace, error) { - return c.Matcher.GetNamespace(name) +func (c *matcher) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + return c.Matcher.GetNamespace(ctx, name) } var _ matching.MatchCriteria = &matchCriteria{} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go index eebe7694340d4..30a6cbebe9793 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/matching/matching.go @@ -17,6 +17,7 @@ limitations under the License. package matching import ( + "context" "fmt" v1 "k8s.io/api/admissionregistration/v1" @@ -44,8 +45,8 @@ type Matcher struct { objectMatcher *object.Matcher } -func (m *Matcher) GetNamespace(name string) (*corev1.Namespace, error) { - return m.namespaceMatcher.GetNamespace(name) +func (m *Matcher) GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { + return m.namespaceMatcher.GetNamespace(ctx, name) } // NewMatcher initialize the matcher with dependencies requires diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go index 153b268d6718b..53dbd6486f08a 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/mutating/dispatcher.go @@ -122,8 +122,12 @@ func (d *dispatcher) dispatchInvocations( // if it is cluster scoped, namespaceName will be empty // Otherwise, get the Namespace resource. if namespaceName != "" { - namespace, err = d.matcher.GetNamespace(namespaceName) + namespace, err = d.matcher.GetNamespace(ctx, namespaceName) if err != nil { + var statusError *k8serrors.StatusError + if errors.As(err, &statusError) { + return nil, statusError + } return nil, k8serrors.NewNotFound(schema.GroupResource{Group: "", Resource: "namespaces"}, namespaceName) } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/admission_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/admission_test.go index 04ade16d145b4..378f2903c6341 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/admission_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/admission_test.go @@ -269,7 +269,7 @@ func (f *fakeMatcher) ValidateInitialization() error { return nil } -func (f *fakeMatcher) GetNamespace(name string) (*v1.Namespace, error) { +func (f *fakeMatcher) GetNamespace(ctx context.Context, name string) (*v1.Namespace, error) { return nil, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go index 8f3e22f64dc28..0b5474b756422 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/policy/validating/dispatcher.go @@ -189,7 +189,7 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm // if it is cluster scoped, namespaceName will be empty // Otherwise, get the Namespace resource. if namespaceName != "" { - namespace, err = c.matcher.GetNamespace(namespaceName) + namespace, err = c.matcher.GetNamespace(ctx, namespaceName) if err != nil { return err } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go index 7817cb17725c2..d73c69bbab150 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace/matcher.go @@ -44,8 +44,13 @@ type Matcher struct { Client clientset.Interface } -func (m *Matcher) GetNamespace(name string) (*v1.Namespace, error) { - return m.NamespaceLister.Get(name) +func (m *Matcher) GetNamespace(ctx context.Context, name string) (*v1.Namespace, error) { + ns, err := m.NamespaceLister.Get(name) + if apierrors.IsNotFound(err) && len(name) > 0 { + // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not + ns, err = m.Client.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + } + return ns, err } // Validate checks if the Matcher has a NamespaceLister and Client. diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement.go b/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement.go index 44a5ddcc5e417..81e4899f0850d 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement.go @@ -106,10 +106,22 @@ func (s *APIEnablementOptions) ApplyTo(c *server.Config, defaultResourceConfig * c.MergedResourceConfig = mergedResourceConfig - if binVersion, emulatedVersion := c.EffectiveVersion.BinaryVersion(), c.EffectiveVersion.EmulationVersion(); !binVersion.EqualTo(emulatedVersion) { + binVersion, emulatedVersion := c.EffectiveVersion.BinaryVersion(), c.EffectiveVersion.EmulationVersion() + if binVersion != nil && emulatedVersion != nil && (binVersion.Major() != emulatedVersion.Major() || binVersion.Minor() != emulatedVersion.Minor()) { for _, version := range registry.PrioritizedVersionsAllGroups() { if strings.Contains(version.Version, "alpha") { - klog.Warningf("alpha api enabled with emulated version %s instead of the binary's version %s, this is unsupported, proceed at your own risk: api=%s", emulatedVersion, binVersion, version.String()) + // Check if this alpha API is actually enabled before warning + entireVersionEnabled := c.MergedResourceConfig.ExplicitGroupVersionConfigs[version] + individualResourceEnabled := false + for resource, enabled := range c.MergedResourceConfig.ExplicitResourceConfigs { + if enabled && resource.Group == version.Group && resource.Version == version.Version { + individualResourceEnabled = true + break + } + } + if entireVersionEnabled || individualResourceEnabled { + klog.Warningf("alpha api enabled with emulated version %s instead of the binary's version %s, this is unsupported, proceed at your own risk: api=%s", emulatedVersion, binVersion, version.String()) + } } } } diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement_test.go b/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement_test.go index a14319e537358..b93771633f1ae 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement_test.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/api_enablement_test.go @@ -17,11 +17,19 @@ limitations under the License. package options import ( + "bytes" "strings" "testing" + "k8s.io/apimachinery/pkg/runtime/schema" utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/version" + apimachineryversion "k8s.io/apimachinery/pkg/version" + "k8s.io/apiserver/pkg/server" + serverstore "k8s.io/apiserver/pkg/server/storage" cliflag "k8s.io/component-base/cli/flag" + "k8s.io/component-base/compatibility" + "k8s.io/klog/v2" ) type fakeGroupRegistry struct{} @@ -77,3 +85,292 @@ func TestAPIEnablementOptionsValidate(t *testing.T) { }) } } + +type fakeGroupVersionRegistry struct { + versions []schema.GroupVersion +} + +func (f fakeGroupVersionRegistry) PrioritizedVersionsAllGroups() []schema.GroupVersion { + return f.versions +} + +func (f fakeGroupVersionRegistry) PrioritizedVersionsForGroup(group string) []schema.GroupVersion { + var result []schema.GroupVersion + for _, gv := range f.versions { + if gv.Group == group { + result = append(result, gv) + } + } + return result +} + +func (f fakeGroupVersionRegistry) IsGroupRegistered(group string) bool { + for _, gv := range f.versions { + if gv.Group == group { + return true + } + } + return false +} + +func (f fakeGroupVersionRegistry) IsVersionRegistered(gv schema.GroupVersion) bool { + for _, version := range f.versions { + if version == gv { + return true + } + } + return false +} + +func (f fakeGroupVersionRegistry) GroupVersions() []schema.GroupVersion { + return f.versions +} + +type fakeEffectiveVersion struct { + binaryVersion *version.Version + emulationVersion *version.Version +} + +func (f fakeEffectiveVersion) BinaryVersion() *version.Version { + return f.binaryVersion +} + +func (f fakeEffectiveVersion) EmulationVersion() *version.Version { + return f.emulationVersion +} + +func (f fakeEffectiveVersion) MinCompatibilityVersion() *version.Version { + return nil +} + +func (f fakeEffectiveVersion) EqualTo(other compatibility.EffectiveVersion) bool { + return f.binaryVersion.EqualTo(other.BinaryVersion()) && f.emulationVersion.EqualTo(other.EmulationVersion()) +} + +func (f fakeEffectiveVersion) String() string { + return "fake" +} + +func (f fakeEffectiveVersion) Info() *apimachineryversion.Info { + return nil +} + +func (f fakeEffectiveVersion) AllowedEmulationVersionRange() string { + return "fake range" +} + +func (f fakeEffectiveVersion) AllowedMinCompatibilityVersionRange() string { + return "fake range" +} + +func (f fakeEffectiveVersion) Validate() []error { + return nil +} + +func TestAPIEnablementOptionsApplyToVersionComparison(t *testing.T) { + // Helper function to capture klog output + captureKlogOutput := func(fn func()) string { + var buf bytes.Buffer + klog.SetOutput(&buf) + klog.LogToStderr(false) + defer func() { + klog.SetOutput(nil) + klog.LogToStderr(true) + }() + + fn() + klog.Flush() + return buf.String() + } + + testCases := []struct { + name string + binaryVersion string + emulationVersion string + alphaAPIsPresent bool + versionEnabled bool + expectWarning bool + expectWarningContent string + }{ + { + name: "same major.minor versions, different patch - no warning", + binaryVersion: "1.34.1", + emulationVersion: "1.34.0", + alphaAPIsPresent: true, + versionEnabled: true, + expectWarning: false, + }, + { + name: "same major.minor versions, no patch in emulation - no warning", + binaryVersion: "1.34.1", + emulationVersion: "1.34", + alphaAPIsPresent: true, + versionEnabled: true, + expectWarning: false, + }, + { + name: "identical versions - no warning", + binaryVersion: "1.34.1", + emulationVersion: "1.34.1", + alphaAPIsPresent: true, + versionEnabled: true, + expectWarning: false, + }, + { + name: "different major versions but not enabled - should not warn", + binaryVersion: "1.34.1", + emulationVersion: "1.33.0", + alphaAPIsPresent: true, + expectWarning: false, + }, + { + name: "different major versions - should warn", + binaryVersion: "1.34.1", + emulationVersion: "1.33.0", + alphaAPIsPresent: true, + expectWarning: true, + versionEnabled: true, + expectWarningContent: "alpha api enabled with emulated version", + }, + { + name: "different minor versions - should warn", + binaryVersion: "1.34.1", + emulationVersion: "1.33.5", + alphaAPIsPresent: true, + expectWarning: true, + versionEnabled: true, + expectWarningContent: "alpha api enabled with emulated version", + }, + { + name: "different major.minor but no alpha APIs - no warning", + binaryVersion: "1.34.1", + emulationVersion: "1.33.0", + alphaAPIsPresent: false, + expectWarning: false, + versionEnabled: true, + }, + { + name: "same major.minor with alpha APIs - no warning", + binaryVersion: "1.34.5", + emulationVersion: "1.34.0", + alphaAPIsPresent: true, + expectWarning: false, + versionEnabled: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + binaryVer := version.MustParse(tc.binaryVersion) + emulationVer := version.MustParse(tc.emulationVersion) + + effectiveVersion := fakeEffectiveVersion{ + binaryVersion: binaryVer, + emulationVersion: emulationVer, + } + + var versions []schema.GroupVersion + if tc.alphaAPIsPresent { + versions = []schema.GroupVersion{ + {Group: "rbac.authorization.k8s.io", Version: "v1alpha1"}, + {Group: "storage.k8s.io", Version: "v1alpha1"}, + } + } else { + versions = []schema.GroupVersion{ + {Group: "rbac.authorization.k8s.io", Version: "v1"}, + {Group: "storage.k8s.io", Version: "v1beta1"}, + } + } + + registry := fakeGroupVersionRegistry{versions: versions} + config := &server.Config{EffectiveVersion: effectiveVersion} + options := &APIEnablementOptions{RuntimeConfig: make(cliflag.ConfigurationMap)} + + // Enable the API + resourceConfig := serverstore.NewResourceConfig() + if tc.versionEnabled { + resourceConfig.ExplicitGroupVersionConfigs[schema.GroupVersion{Group: "rbac.authorization.k8s.io", Version: "v1alpha1"}] = true + } + + // Capture log output during ApplyTo execution + logOutput := captureKlogOutput(func() { + err := options.ApplyTo(config, resourceConfig, registry) + if err != nil { + t.Errorf("ApplyTo failed: %v", err) + } + }) + + // Verify warning expectations + if tc.expectWarning { + if !strings.Contains(logOutput, tc.expectWarningContent) { + t.Errorf("Expected warning containing '%s', but got log output: %s", tc.expectWarningContent, logOutput) + } + if !strings.Contains(logOutput, "W") { // klog warning prefix + t.Errorf("Expected warning log level, but got log output: %s", logOutput) + } + } else if strings.Contains(logOutput, "alpha api enabled") { + t.Errorf("Expected no warning, but got log output: %s", logOutput) + } + }) + } +} + +func TestAPIEnablementOptionsApplyToErrorCases(t *testing.T) { + // Create a default effective version for test configs + defaultEffectiveVersion := fakeEffectiveVersion{ + binaryVersion: version.MustParse("1.34.0"), + emulationVersion: version.MustParse("1.34.0"), + } + + testCases := []struct { + name string + options *APIEnablementOptions + config *server.Config + expectError bool + errorContains string + }{ + { + name: "nil options should not error", + options: nil, + config: &server.Config{ + EffectiveVersion: defaultEffectiveVersion, + }, + expectError: false, + }, + { + name: "invalid runtime config value should error", + options: &APIEnablementOptions{ + RuntimeConfig: cliflag.ConfigurationMap{ + "api/all": "invalid-value", // Must be "true" or "false" + }, + }, + config: &server.Config{ + EffectiveVersion: defaultEffectiveVersion, + }, + expectError: true, + errorContains: "invalid value", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + registry := fakeGroupVersionRegistry{versions: []schema.GroupVersion{ + {Group: "rbac.authorization.k8s.io", Version: "v1"}, + }} + + err := tc.options.ApplyTo(tc.config, serverstore.NewResourceConfig(), registry) + + if tc.expectError { + if err == nil { + t.Errorf("Expected error but got none") + } else if tc.errorContains != "" && !strings.Contains(err.Error(), tc.errorContains) { + t.Errorf("Expected error containing '%s', but got: %v", tc.errorContains, err) + } + } else { + if err != nil { + t.Errorf("Expected no error but got: %v", err) + } + } + }) + } +} diff --git a/staging/src/k8s.io/client-go/tools/cache/controller.go b/staging/src/k8s.io/client-go/tools/cache/controller.go index 5f983b6b63a0a..e07c04e6252be 100644 --- a/staging/src/k8s.io/client-go/tools/cache/controller.go +++ b/staging/src/k8s.io/client-go/tools/cache/controller.go @@ -596,16 +596,7 @@ func newInformer(clientState Store, options InformerOptions) Controller { // KeyLister, that way resync operations will result in the correct set // of update/delete deltas. - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, clientState, options.Transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: clientState, - EmitDeltaTypeReplaced: true, - Transformer: options.Transform, - }) - } + fifo := newQueueFIFO(clientState, options.Transform) cfg := &Config{ Queue: fifo, @@ -623,3 +614,15 @@ func newInformer(clientState Store, options InformerOptions) Controller { } return New(cfg) } + +func newQueueFIFO(clientState Store, transform TransformFunc) Queue { + if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { + return NewRealFIFO(MetaNamespaceKeyFunc, clientState, transform) + } else { + return NewDeltaFIFOWithOptions(DeltaFIFOOptions{ + KnownObjects: clientState, + EmitDeltaTypeReplaced: true, + Transformer: transform, + }) + } +} diff --git a/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go b/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go index 9d9e238cccedd..a0d7a834aa0da 100644 --- a/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go +++ b/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go @@ -270,7 +270,8 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { } var ( - _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue + _ = TransformingStore(&DeltaFIFO{}) // DeltaFIFO implements TransformingStore to allow memory optimizations ) var ( diff --git a/staging/src/k8s.io/client-go/tools/cache/delta_fifo_test.go b/staging/src/k8s.io/client-go/tools/cache/delta_fifo_test.go index 9c67012cc1c57..24f0ec87d04bb 100644 --- a/staging/src/k8s.io/client-go/tools/cache/delta_fifo_test.go +++ b/staging/src/k8s.io/client-go/tools/cache/delta_fifo_test.go @@ -28,7 +28,7 @@ import ( // from the most recent Delta. // You should treat the items returned inside the deltas as immutable. // This function was moved here because it is not consistent with normal list semantics, but is used in unit testing. -func (f *DeltaFIFO) List() []interface{} { +func (f *DeltaFIFO) list() []interface{} { f.lock.RLock() defer f.lock.RUnlock() return f.listLocked() @@ -46,7 +46,7 @@ func (f *DeltaFIFO) listLocked() []interface{} { // ListKeys returns a list of all the keys of the objects currently // in the FIFO. // This function was moved here because it is not consistent with normal list semantics, but is used in unit testing. -func (f *DeltaFIFO) ListKeys() []string { +func (f *DeltaFIFO) listKeys() []string { f.lock.RLock() defer f.lock.RUnlock() list := make([]string, 0, len(f.queue)) @@ -60,19 +60,19 @@ func (f *DeltaFIFO) ListKeys() []string { // or sets exists=false. // You should treat the items returned inside the deltas as immutable. // This function was moved here because it is not consistent with normal list semantics, but is used in unit testing. -func (f *DeltaFIFO) Get(obj interface{}) (item interface{}, exists bool, err error) { +func (f *DeltaFIFO) get(obj interface{}) (item interface{}, exists bool, err error) { key, err := f.KeyOf(obj) if err != nil { return nil, false, KeyError{obj, err} } - return f.GetByKey(key) + return f.getByKey(key) } // GetByKey returns the complete list of deltas for the requested item, // setting exists=false if that list is empty. // You should treat the items returned inside the deltas as immutable. // This function was moved here because it is not consistent with normal list semantics, but is used in unit testing. -func (f *DeltaFIFO) GetByKey(key string) (item interface{}, exists bool, err error) { +func (f *DeltaFIFO) getByKey(key string) (item interface{}, exists bool, err error) { f.lock.RLock() defer f.lock.RUnlock() d, exists := f.items[key] @@ -320,10 +320,10 @@ func TestDeltaFIFO_addUpdate(t *testing.T) { f.Update(mkFifoObj("foo", 12)) f.Delete(mkFifoObj("foo", 15)) - if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) { + if e, a := []interface{}{mkFifoObj("foo", 15)}, f.list(); !reflect.DeepEqual(e, a) { t.Errorf("Expected %+v, got %+v", e, a) } - if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) { + if e, a := []string{"foo"}, f.listKeys(); !reflect.DeepEqual(e, a) { t.Errorf("Expected %+v, got %+v", e, a) } @@ -349,7 +349,7 @@ func TestDeltaFIFO_addUpdate(t *testing.T) { t.Errorf("Got second value %v", unexpected.val) case <-time.After(50 * time.Millisecond): } - _, exists, _ := f.Get(mkFifoObj("foo", "")) + _, exists, _ := f.get(mkFifoObj("foo", "")) if exists { t.Errorf("item did not get removed") } @@ -397,7 +397,7 @@ func TestDeltaFIFO_transformer(t *testing.T) { must(f.Replace([]interface{}{}, "")) // Should be empty - if e, a := []string{"foo", "bar"}, f.ListKeys(); !reflect.DeepEqual(e, a) { + if e, a := []string{"foo", "bar"}, f.listKeys(); !reflect.DeepEqual(e, a) { t.Errorf("Expected %+v, got %+v", e, a) } @@ -507,7 +507,7 @@ func TestDeltaFIFO_addReplace(t *testing.T) { t.Errorf("Got second value %v", unexpected.val) case <-time.After(50 * time.Millisecond): } - _, exists, _ := f.Get(mkFifoObj("foo", "")) + _, exists, _ := f.get(mkFifoObj("foo", "")) if exists { t.Errorf("item did not get removed") } @@ -991,7 +991,7 @@ func BenchmarkDeltaFIFOListKeys(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _ = f.ListKeys() + _ = f.listKeys() } }) b.StopTimer() diff --git a/staging/src/k8s.io/client-go/tools/cache/reflector.go b/staging/src/k8s.io/client-go/tools/cache/reflector.go index ee9be77278ab4..6fd43375f60b4 100644 --- a/staging/src/k8s.io/client-go/tools/cache/reflector.go +++ b/staging/src/k8s.io/client-go/tools/cache/reflector.go @@ -80,7 +80,7 @@ type ReflectorStore interface { // TransformingStore is an optional interface that can be implemented by the provided store. // If implemented on the provided store reflector will use the same transformer in its internal stores. type TransformingStore interface { - Store + ReflectorStore Transformer() TransformFunc } @@ -726,9 +726,11 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { return false } + var transformer TransformFunc storeOpts := []StoreOption{} if tr, ok := r.store.(TransformingStore); ok && tr.Transformer() != nil { - storeOpts = append(storeOpts, WithTransformer(tr.Transformer())) + transformer = tr.Transformer() + storeOpts = append(storeOpts, WithTransformer(transformer)) } initTrace := trace.New("Reflector WatchList", trace.Field{Key: "name", Value: r.name}) @@ -788,7 +790,7 @@ func (r *Reflector) watchList(ctx context.Context) (watch.Interface, error) { // we utilize the temporaryStore to ensure independence from the current store implementation. // as of today, the store is implemented as a queue and will be drained by the higher-level // component as soon as it finishes replacing the content. - checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, temporaryStore.List) + checkWatchListDataConsistencyIfRequested(ctx, r.name, resourceVersion, r.listerWatcher.ListWithContext, transformer, temporaryStore.List) if err := r.store.Replace(temporaryStore.List(), resourceVersion); err != nil { return nil, fmt.Errorf("unable to sync watch-list result: %w", err) diff --git a/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go b/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go index a7e0d9c436859..4119c78a6c0cc 100644 --- a/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go +++ b/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector.go @@ -33,11 +33,11 @@ import ( // // Note that this function will panic when data inconsistency is detected. // This is intentional because we want to catch it in the CI. -func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { +func checkWatchListDataConsistencyIfRequested[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn consistencydetector.ListFunc[T], listItemTransformFunc func(interface{}) (interface{}, error), retrieveItemsFn consistencydetector.RetrieveItemsFunc[U]) { if !consistencydetector.IsDataConsistencyDetectionForWatchListEnabled() { return } // for informers we pass an empty ListOptions because // listFn might be wrapped for filtering during informer construction. - consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, metav1.ListOptions{}, retrieveItemsFn) + consistencydetector.CheckDataConsistency(ctx, identity, lastSyncedResourceVersion, listFn, listItemTransformFunc, metav1.ListOptions{}, retrieveItemsFn) } diff --git a/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector_test.go b/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector_test.go new file mode 100644 index 0000000000000..1d18d0be180d0 --- /dev/null +++ b/staging/src/k8s.io/client-go/tools/cache/reflector_data_consistency_detector_test.go @@ -0,0 +1,114 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "context" + "fmt" + "testing" + "time" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + clientfeatures "k8s.io/client-go/features" + clientfeaturestesting "k8s.io/client-go/features/testing" + "k8s.io/client-go/util/consistencydetector" + "k8s.io/klog/v2/ktesting" +) + +func TestReflectorDataConsistencyDetector(t *testing.T) { + clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, true) + restore := consistencydetector.SetDataConsistencyDetectionForWatchListEnabledForTest(true) + defer restore() + + markTransformed := func(obj interface{}) (interface{}, error) { + pod, ok := obj.(*v1.Pod) + if !ok { + return obj, nil + } + newPod := pod.DeepCopy() + if newPod.Labels == nil { + newPod.Labels = make(map[string]string) + } + newPod.Labels["transformed"] = "true" + return newPod, nil + } + + for _, inOrder := range []bool{false, true} { + t.Run(fmt.Sprintf("InOrder=%v", inOrder), func(t *testing.T) { + clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.InOrderInformers, inOrder) + for _, transformerEnabled := range []bool{false, true} { + var transformer TransformFunc + if transformerEnabled { + transformer = markTransformed + } + t.Run(fmt.Sprintf("Transformer=%v", transformerEnabled), func(t *testing.T) { + runTestReflectorDataConsistencyDetector(t, transformer) + }) + } + }) + } +} + +func runTestReflectorDataConsistencyDetector(t *testing.T, transformer TransformFunc) { + _, ctx := ktesting.NewTestContext(t) + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + store := NewStore(MetaNamespaceKeyFunc) + fifo := newQueueFIFO(store, transformer) + + lw := &ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return &v1.PodList{ + ListMeta: metav1.ListMeta{ResourceVersion: "1"}, + Items: []v1.Pod{ + {ObjectMeta: metav1.ObjectMeta{Name: "pod-1", ResourceVersion: "1"}}, + }, + }, nil + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + w := watch.NewFake() + go func() { + w.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod-1", ResourceVersion: "1"}}) + w.Action(watch.Bookmark, &v1.Pod{ObjectMeta: metav1.ObjectMeta{ + Name: "pod-1", + ResourceVersion: "1", + Annotations: map[string]string{metav1.InitialEventsAnnotationKey: "true"}, + }}) + }() + return w, nil + }, + } + + r := NewReflector(lw, &v1.Pod{}, fifo, 0) + + go func() { + _ = wait.PollUntilContextTimeout(ctx, 10*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { + return r.LastSyncResourceVersion() != "", nil + }) + cancel() + }() + + err := r.ListAndWatchWithContext(ctx) + if err != nil { + t.Errorf("ListAndWatchWithContext returned error: %v", err) + } +} diff --git a/staging/src/k8s.io/client-go/tools/cache/reflector_test.go b/staging/src/k8s.io/client-go/tools/cache/reflector_test.go index 8c20ae0b02a4e..2c1965dd1a62a 100644 --- a/staging/src/k8s.io/client-go/tools/cache/reflector_test.go +++ b/staging/src/k8s.io/client-go/tools/cache/reflector_test.go @@ -20,10 +20,12 @@ import ( "context" "errors" "fmt" + "maps" "math/rand" "net/http" "reflect" goruntime "runtime" + "slices" "strconv" "sync" "sync/atomic" @@ -1962,7 +1964,7 @@ func TestReflectorReplacesStoreOnUnsafeDelete(t *testing.T) { s := NewFIFO(MetaNamespaceKeyFunc) var replaceInvoked atomic.Int32 store := &fakeStore{ - Store: s, + ReflectorStore: s, beforeReplace: func(list []interface{}, rv string) { // interested in the Replace call that happens after the Error event if rv == lastExpectedRV { @@ -2057,131 +2059,165 @@ func TestReflectorReplacesStoreOnUnsafeDelete(t *testing.T) { } func TestReflectorRespectStoreTransformer(t *testing.T) { - mkPod := func(id string, rv string) *v1.Pod { - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: id, ResourceVersion: rv}, - Spec: v1.PodSpec{ - Hostname: "test", + for name, test := range map[string]struct { + storeBuilder func(counter *atomic.Int32) ReflectorStore + items func(rs ReflectorStore) []interface{} + }{ + "real-fifo": { + storeBuilder: func(counter *atomic.Int32) ReflectorStore { + return NewRealFIFO(MetaNamespaceKeyFunc, NewStore(MetaNamespaceKeyFunc), func(i interface{}) (interface{}, error) { + counter.Add(1) + cast := i.(*v1.Pod) + cast.Spec.Hostname = "transformed" + return cast, nil + }) }, - } - } - - preExisting1 := mkPod("foo-1", "1") - preExisting2 := mkPod("foo-2", "2") - pod3 := mkPod("foo-3", "3") - - lastExpectedRV := "3" - events := []watch.Event{ - {Type: watch.Added, Object: preExisting1}, - {Type: watch.Added, Object: preExisting2}, - {Type: watch.Bookmark, Object: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: lastExpectedRV, - Annotations: map[string]string{ - metav1.InitialEventsAnnotationKey: "true", - }, + items: func(rs ReflectorStore) []interface{} { + store := rs.(*RealFIFO) + objects := make(map[string]interface{}) + for _, item := range store.getItems() { + key, _ := store.keyFunc(item.Object) + if item.Type == Deleted { + delete(objects, key) + } else { + objects[key] = item.Object + } + } + return slices.Collect(maps.Values(objects)) }, - }}, - {Type: watch.Added, Object: pod3}, - } + }, + "delta-fifo": { + storeBuilder: func(counter *atomic.Int32) ReflectorStore { + return NewDeltaFIFOWithOptions(DeltaFIFOOptions{ + KeyFunction: MetaNamespaceKeyFunc, + Transformer: func(i interface{}) (interface{}, error) { + counter.Add(1) + cast := i.(*v1.Pod) + cast.Spec.Hostname = "transformed" + return cast, nil + }, + }) + }, + items: func(rs ReflectorStore) []interface{} { + return rs.(*DeltaFIFO).list() + }, + }, + } { + t.Run(name, func(t *testing.T) { + mkPod := func(id string, rv string) *v1.Pod { + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: id, ResourceVersion: rv}, + Spec: v1.PodSpec{ + Hostname: "test", + }, + } + } - s := NewFIFO(MetaNamespaceKeyFunc) - var replaceInvoked atomic.Int32 - store := &fakeStore{ - Store: s, - beforeReplace: func(list []interface{}, rv string) { - replaceInvoked.Add(1) - // Only two pods are present at the point when Replace is called. - if len(list) != 2 { - t.Errorf("unexpected nb of objects: expected 2 received %d", len(list)) + preExisting1 := mkPod("foo-1", "1") + preExisting2 := mkPod("foo-2", "2") + pod3 := mkPod("foo-3", "3") + + lastExpectedRV := "3" + events := []watch.Event{ + {Type: watch.Added, Object: preExisting1}, + {Type: watch.Added, Object: preExisting2}, + {Type: watch.Bookmark, Object: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: lastExpectedRV, + Annotations: map[string]string{ + metav1.InitialEventsAnnotationKey: "true", + }, + }, + }}, + {Type: watch.Added, Object: pod3}, } - for _, obj := range list { - cast := obj.(*v1.Pod) - if cast.Spec.Hostname != "transformed" { - t.Error("Object was not transformed prior to replacement") - } + + var transformerInvoked atomic.Int32 + s := test.storeBuilder(&transformerInvoked) + + var once sync.Once + lw := &ListWatch{ + WatchFunc: func(metav1.ListOptions) (watch.Interface, error) { + fw := watch.NewFake() + go func() { + once.Do(func() { + for _, e := range events { + fw.Action(e.Type, e.Object) + } + }) + }() + return fw, nil + }, + // ListFunc should never be used in WatchList mode + ListFunc: func(metav1.ListOptions) (runtime.Object, error) { + return nil, errors.New("list call not expected in WatchList mode") + }, } - }, - afterReplace: func(rv string, err error) {}, - transformer: func(i interface{}) (interface{}, error) { - cast := i.(*v1.Pod) - cast.Spec.Hostname = "transformed" - return cast, nil - }, - } - var once sync.Once - lw := &ListWatch{ - WatchFunc: func(metav1.ListOptions) (watch.Interface, error) { - fw := watch.NewFake() + clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, true) + r := NewReflector(lw, &v1.Pod{}, s, 0) + ctx, cancel := context.WithCancel(context.Background()) + doneCh := make(chan struct{}) go func() { - once.Do(func() { - for _, e := range events { - fw.Action(e.Type, e.Object) - } - }) + defer close(doneCh) + r.RunWithContext(ctx) }() - return fw, nil - }, - // ListFunc should never be used in WatchList mode - ListFunc: func(metav1.ListOptions) (runtime.Object, error) { - return nil, errors.New("list call not expected in WatchList mode") - }, - } - clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.WatchListClient, true) - r := NewReflector(lw, &v1.Pod{}, store, 0) - ctx, cancel := context.WithCancel(context.Background()) - doneCh := make(chan struct{}) - go func() { - defer close(doneCh) - r.RunWithContext(ctx) - }() + // wait for the RV to sync to the version returned by the final list + err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (done bool, err error) { + if rv := r.LastSyncResourceVersion(); rv == lastExpectedRV { + return true, nil + } + return false, nil + }) + if err != nil { + t.Fatalf("reflector never caught up with expected revision: %q, err: %v", lastExpectedRV, err) + } - // wait for the RV to sync to the version returned by the final list - err := wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (done bool, err error) { - if rv := r.LastSyncResourceVersion(); rv == lastExpectedRV { - return true, nil - } - return false, nil - }) - if err != nil { - t.Fatalf("reflector never caught up with expected revision: %q, err: %v", lastExpectedRV, err) - } + if want, got := lastExpectedRV, r.LastSyncResourceVersion(); want != got { + t.Errorf("expected LastSyncResourceVersion to be %q, but got: %q", want, got) + } - if want, got := lastExpectedRV, r.LastSyncResourceVersion(); want != got { - t.Errorf("expected LastSyncResourceVersion to be %q, but got: %q", want, got) - } - if want, got := 1, int(replaceInvoked.Load()); want != got { - t.Errorf("expected replace to be invoked %d times, but got: %d", want, got) - } + informerItems := test.items(s) + if want, got := 3, len(informerItems); want != got { + t.Errorf("expected informer to contain %d objects, but got: %d", want, got) + } + for _, item := range informerItems { + cast := item.(*v1.Pod) + if cast.Spec.Hostname != "transformed" { + t.Error("Object was not transformed prior to replacement") + } + } - cancel() - select { - case <-doneCh: - case <-time.After(wait.ForeverTestTimeout): - t.Errorf("timed out waiting for Run to return") + // Transformer should have been invoked twice for the initial sync in the informer on the temporary store, + // then twice on replace, then once on the following update. + if want, got := 5, int(transformerInvoked.Load()); want != got { + t.Errorf("expected transformer to be invoked %d times, but got: %d", want, got) + } + + cancel() + select { + case <-doneCh: + case <-time.After(wait.ForeverTestTimeout): + t.Errorf("timed out waiting for Run to return") + } + }) } } type fakeStore struct { - Store + ReflectorStore beforeReplace func(list []interface{}, s string) afterReplace func(rv string, err error) - transformer TransformFunc } func (f *fakeStore) Replace(list []interface{}, rv string) error { f.beforeReplace(list, rv) - err := f.Store.Replace(list, rv) + err := f.ReflectorStore.Replace(list, rv) f.afterReplace(rv, err) return err } -func (f *fakeStore) Transformer() TransformFunc { - return f.transformer -} - func BenchmarkExtractList(b *testing.B) { _, _, podList := getPodListItems(0, fakeItemsNum) _, _, configMapList := getConfigmapListItems(0, fakeItemsNum) diff --git a/staging/src/k8s.io/client-go/tools/cache/shared_informer.go b/staging/src/k8s.io/client-go/tools/cache/shared_informer.go index 99e5fcd180006..1c12aa2d64a14 100644 --- a/staging/src/k8s.io/client-go/tools/cache/shared_informer.go +++ b/staging/src/k8s.io/client-go/tools/cache/shared_informer.go @@ -539,16 +539,7 @@ func (s *sharedIndexInformer) RunWithContext(ctx context.Context) { s.startedLock.Lock() defer s.startedLock.Unlock() - var fifo Queue - if clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { - fifo = NewRealFIFO(MetaNamespaceKeyFunc, s.indexer, s.transform) - } else { - fifo = NewDeltaFIFOWithOptions(DeltaFIFOOptions{ - KnownObjects: s.indexer, - EmitDeltaTypeReplaced: true, - Transformer: s.transform, - }) - } + fifo := newQueueFIFO(s.indexer, s.transform) cfg := &Config{ Queue: fifo, diff --git a/staging/src/k8s.io/client-go/tools/cache/the_real_fifo.go b/staging/src/k8s.io/client-go/tools/cache/the_real_fifo.go index ef322bea85078..b907410dc05e0 100644 --- a/staging/src/k8s.io/client-go/tools/cache/the_real_fifo.go +++ b/staging/src/k8s.io/client-go/tools/cache/the_real_fifo.go @@ -61,7 +61,8 @@ type RealFIFO struct { } var ( - _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = Queue(&RealFIFO{}) // RealFIFO is a Queue + _ = TransformingStore(&RealFIFO{}) // RealFIFO implements TransformingStore to allow memory optimizations ) // Close the queue. diff --git a/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go b/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go index 06f172d82bfe4..72c0124a0f131 100644 --- a/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go +++ b/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector.go @@ -45,16 +45,28 @@ func IsDataConsistencyDetectionForWatchListEnabled() bool { return dataConsistencyDetectionForWatchListEnabled } +// SetDataConsistencyDetectionForWatchListEnabledForTest allows to enable/disable data consistency detection for testing purposes. +// It returns a function that restores the original value. +func SetDataConsistencyDetectionForWatchListEnabledForTest(enabled bool) func() { + original := dataConsistencyDetectionForWatchListEnabled + dataConsistencyDetectionForWatchListEnabled = enabled + return func() { + dataConsistencyDetectionForWatchListEnabled = original + } +} + type RetrieveItemsFunc[U any] func() []U type ListFunc[T runtime.Object] func(ctx context.Context, options metav1.ListOptions) (T, error) +type TransformFunc func(interface{}) (interface{}, error) + // CheckDataConsistency exists solely for testing purposes. // we cannot use checkWatchListDataConsistencyIfRequested because // it is guarded by an environmental variable. // we cannot manipulate the environmental variable because // it will affect other tests in this package. -func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { +func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity string, lastSyncedResourceVersion string, listFn ListFunc[T], listItemTransformFunc TransformFunc, listOptions metav1.ListOptions, retrieveItemsFn RetrieveItemsFunc[U]) { if !canFormAdditionalListCall(lastSyncedResourceVersion, listOptions) { klog.V(4).Infof("data consistency check for %s is enabled but the parameters (RV, ListOptions) doesn't allow for creating a valid LIST request. Skipping the data consistency check.", identity) return @@ -84,6 +96,15 @@ func CheckDataConsistency[T runtime.Object, U any](ctx context.Context, identity if err != nil { panic(err) // this should never happen } + if listItemTransformFunc != nil { + for i := range rawListItems { + obj, err := listItemTransformFunc(rawListItems[i]) + if err != nil { + panic(err) + } + rawListItems[i] = obj.(runtime.Object) + } + } listItems := toMetaObjectSliceOrDie(rawListItems) sort.Sort(byUID(listItems)) diff --git a/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector_test.go b/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector_test.go index bdac197a756a5..a5e8bffe73aee 100644 --- a/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector_test.go +++ b/staging/src/k8s.io/client-go/util/consistencydetector/data_consistency_detector_test.go @@ -215,10 +215,10 @@ func TestDataConsistencyChecker(t *testing.T) { if scenario.expectPanic { require.Panics(t, func() { - CheckDataConsistency(ctx, "", scenario.lastSyncedResourceVersion, fakeLister.List, scenario.requestOptions, retrievedItemsFunc) + CheckDataConsistency(ctx, "", scenario.lastSyncedResourceVersion, fakeLister.List, nil, scenario.requestOptions, retrievedItemsFunc) }) } else { - CheckDataConsistency(ctx, "", scenario.lastSyncedResourceVersion, fakeLister.List, scenario.requestOptions, retrievedItemsFunc) + CheckDataConsistency(ctx, "", scenario.lastSyncedResourceVersion, fakeLister.List, nil, scenario.requestOptions, retrievedItemsFunc) } require.Equal(t, scenario.expectedListRequests, fakeLister.counter) @@ -235,7 +235,7 @@ func TestDataConsistencyCheckerRetry(t *testing.T) { stopListErrorAfter := 5 fakeErrLister := &errorLister{stopErrorAfter: stopListErrorAfter} - CheckDataConsistency(ctx, "", "", fakeErrLister.List, metav1.ListOptions{}, retrievedItemsFunc) + CheckDataConsistency(ctx, "", "", fakeErrLister.List, nil, metav1.ListOptions{}, retrievedItemsFunc) require.Equal(t, fakeErrLister.listCounter, fakeErrLister.stopErrorAfter) } diff --git a/test/utils/image/manifest.go b/test/utils/image/manifest.go index 242b286f947b8..85629d3806061 100644 --- a/test/utils/image/manifest.go +++ b/test/utils/image/manifest.go @@ -221,7 +221,7 @@ func initImageConfigs(list RegistryList) (map[ImageID]Config, map[ImageID]Config configs[APIServer] = Config{list.PromoterE2eRegistry, "sample-apiserver", "1.29.2"} configs[AppArmorLoader] = Config{list.PromoterE2eRegistry, "apparmor-loader", "1.4"} configs[BusyBox] = Config{list.PromoterE2eRegistry, "busybox", "1.37.0-1"} - configs[DistrolessIptables] = Config{list.BuildImageRegistry, "distroless-iptables", "v0.7.11"} + configs[DistrolessIptables] = Config{list.BuildImageRegistry, "distroless-iptables", "v0.7.13"} configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.6.4-0"} configs[Httpd] = Config{list.PromoterE2eRegistry, "httpd", "2.4.38-4"} configs[HttpdNew] = Config{list.PromoterE2eRegistry, "httpd", "2.4.39-4"}