diff --git a/.ci-operator.yaml b/.ci-operator.yaml index e307e5af6628b..a3628cf240a63 100644 --- a/.ci-operator.yaml +++ b/.ci-operator.yaml @@ -1,4 +1,4 @@ build_root_image: name: release namespace: openshift - tag: rhel-9-release-golang-1.24-openshift-4.21 + tag: rhel-9-release-golang-1.25-openshift-4.22 diff --git a/.go-version b/.go-version index eb716f77a7b8d..198ec23ccfcc9 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.24.9 +1.25.6 diff --git a/CHANGELOG/CHANGELOG-1.34.md b/CHANGELOG/CHANGELOG-1.34.md deleted file mode 100644 index 336f60a6e28cc..0000000000000 --- a/CHANGELOG/CHANGELOG-1.34.md +++ /dev/null @@ -1,2214 +0,0 @@ - - -- [v1.34.1](#v1341) - - [Downloads for v1.34.1](#downloads-for-v1341) - - [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) - - [Changes by Kind](#changes-by-kind) - - [Bug or Regression](#bug-or-regression) - - [Dependencies](#dependencies) - - [Added](#added) - - [Changed](#changed) - - [Removed](#removed) -- [v1.34.0](#v1340) - - [Downloads for v1.34.0](#downloads-for-v1340) - - [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) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-2) - - [Feature](#feature-1) - - [Documentation](#documentation) - - [Bug or Regression](#bug-or-regression-2) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-3) - - [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) - - [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) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-5) - - [API Change](#api-change-2) - - [Feature](#feature-3) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-6) - - [API Change](#api-change-3) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-7) - - [Deprecation](#deprecation-2) - - [API Change](#api-change-4) - - [Feature](#feature-5) - - [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) - - [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) - - [Changes by Kind](#changes-by-kind-8) - - [Deprecation](#deprecation-3) - - [API Change](#api-change-5) - - [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.1 - - -## Downloads for v1.34.1 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes.tar.gz) | b1262f114376f7bc0532ef688e758657ada0796e958c7b49e1401e8a2789791a7d59e5460c54780131fc8fa7398c6e87a7e59fdc4a84061c15d015c69a07e10d -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-src.tar.gz) | 5109cd698bd249341357f5a0b7ab3cd078a641747ef1a17e168f650c62af854cc46bf3bca884f43ea33d51e81a2be4e31d0d02af639a3f58d79f3f1322b0e238 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-darwin-amd64.tar.gz) | c977b7ede3a07ec721a874ec127a9b2d2e1edce097e33fc5bfe0a7a2ecf61153c4e514787e89003eeb8d463f47ba0c09f3267669769f0cba873c5265674e056d -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-darwin-arm64.tar.gz) | ae6b112e45e50a9d1ce0738f948f933eed419dde20a70f399cfcf77ebf5179b6af893ae7e1e633f5b99c1f34a499a2238474cc45878afdf250c048ea43c559a2 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-386.tar.gz) | 3e8aff795fa394343b4d3a943dba25b06b5c122df91fe5893cb354ee605a087f6150cee6225ff60d4b1ed9e0fa02adb9e4ccd8e38cd12337a92cedbdcfaabff2 -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-amd64.tar.gz) | 3abedd362fffd5eb749febdeb59c2edd9902f7f69fb182f879daeb27cc88405983c539513cb74ef9b9587ab3829bde992f22f2067fd181311989345f6e13b867 -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-arm.tar.gz) | 0d28e96ff4bf3f570277f194a975c19e8a1b49e7240908a91278647c44b5f019251dd7774aed5dbbfe7c030ded993701044c90ac97e14de5c51d0e9ae84d2127 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-arm64.tar.gz) | 279832e1ac95532807aeb68ed951e8099300e3cd4a09f1d829c4b0197e0010d18d1de19e54f73b0ab7f104ee5670ef4897127432fac42867b7a727d75dc8bd48 -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-ppc64le.tar.gz) | 1367d4dfebab6f504612d6aa7e6dd7f6391ec28779c0610ef89c77bb691a5020ff3d863d5414645d62e9dfbf1fe814cf8b3bae3097c210f8e8ad895deb19c291 -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-linux-s390x.tar.gz) | d03ff4bbad2c947a37a6ffc62f3db08cf2cc1d9d702d90b94f80fb9fdcc637c4f96096beb3a466f07ac4ca807d89e81240f15cf7d2ae1c6fbd4a953122728e28 -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-windows-386.tar.gz) | 7929fd442acfa851c1510b52a6c3a11f6d3c2fb318597e68134a1927bac18ab70c6de7d572c0c05ecbc8c5764cf20fc91ab4c1ad604c7cd3707b86c01cb9fd16 -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-windows-amd64.tar.gz) | f73e914d28e0986d4b32bbf0d39c428d3e4d28dac11cf8d2b48eae4f1825511fc8b1b706427a1fe752fc0d280f1b4c539f4261cc31f679f25646ac5234afa7ad -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-client-windows-arm64.tar.gz) | f03de193bc851a1327cbc7338f019cabe7167775ca597c36637b10332b8892a7a4bcc5daa090349f24347f5210fced19c7a15211c69abb94fee87e88c1efaa30 - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-server-linux-amd64.tar.gz) | 8fd1e779f4d0188592644e234a6e5b728b9000a2afeb9d8da25131a5a4e54718bb46c4d521c62e26ea971e32745529fbb001c4f011ef2c54091cb5e81b4b90f2 -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-server-linux-arm64.tar.gz) | 77f68803b34f710c9623f388452494075ca9bb38567e7878176ec12a6d2971d2feba381e99462dc8c6e83ff5064dcffcaa7df736b67208880f5e90d71a831c2c -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-server-linux-ppc64le.tar.gz) | 6a5378a02b9b27cce9e0bc26399f8c0a8676372407bb618949fa41caacb4bbfbc7ec5487e00d973fbf409abe848a3aed42b2ead2c78753a1dd7c3251daf61745 -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-server-linux-s390x.tar.gz) | 6b9b4b64907ec817ce93a70faecbfcccf665e6b7681d0c21e26844c9d2645227ee8956c3b6b6a2417725b1e64353d5e1ed7071cf2c8e71ea8551cd47d662c3d8 - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-node-linux-amd64.tar.gz) | c9b7d52708c4282757cd7aaa8b059c26f8f427cf8c238dff95cdc85a68d42c28b6e09fbf1aee3fa6f5f377aa395c6b9a73c112c56a6485e22b16a9c8562a8eef -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-node-linux-arm64.tar.gz) | efe54933eb5e7e6b44c76efe0b4cec911340793ef2eafdd595593fb2537e5704429d3a291793cb69ad459fe14058da491a29a12d963ba34ee4c1475cc0799b0f -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-node-linux-ppc64le.tar.gz) | 59a7223e167c890d8cb8544b9692182aaccb3814cb203337ea21a87902e0174d6f0e114d015989c42890d3b73cb73bdf8b1b71ef89fd1b0cf615349d10c23f8f -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-node-linux-s390x.tar.gz) | b648658aaae4812d787b7be04bdfd13dc379316bbcda107eca410ffbdf57713f00bbb68ad4fe9501c3bb26e5d35f589653d4067a5753f681e41f493a28309ea9 -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.1/kubernetes-node-windows-amd64.tar.gz) | 4c70f856364a976aa919662f3b3f6f06da3fe7ae156b7bf3fd84de4b5a0b0c70221283220c48c3cc31dddce0f2e0167606126515b1750ca90aaf129f1c9280ce - -### 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.1](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.1](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.1](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.1](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.1](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.1](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.0 - -## Changes by Kind - -### Bug or Regression - -- Fixed SELinux warning controller not emitting events on some SELinux label conflicts. ([#133745](https://github.com/kubernetes/kubernetes/pull/133745), [@jsafrane](https://github.com/jsafrane)) [SIG Apps, Storage and Testing] -- Fixed broken shell completion for api resources. ([#133783](https://github.com/kubernetes/kubernetes/pull/133783), [@vpnachev](https://github.com/vpnachev)) [SIG CLI] -- Kube-apiserver: Fixed a 1.34 regression in CustomResourceDefinition handling that incorrectly warned about unrecognized formats on number and integer properties ([#133901](https://github.com/kubernetes/kubernetes/pull/133901), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery] -- Kube-apiserver: Fixes a 1.34 regression with spurious "Error getting keys" log messages ([#133866](https://github.com/kubernetes/kubernetes/pull/133866), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Kube-apiserver: Fixes a possible 1.34 performance regression calculating object size statistics for resources not served from the watch cache, typically only Events ([#133879](https://github.com/kubernetes/kubernetes/pull/133879), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Kubeadm: fixed bug where v1beta3's ClusterConfiguration.APIServer.TimeoutForControlPlane is not respected in newer versions of kubeadm where v1beta4 is the default. ([#133753](https://github.com/kubernetes/kubernetes/pull/133753), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Cluster Lifecycle] - -## Dependencies - -### Added -_Nothing has changed._ - -### Changed -_Nothing has changed._ - -### Removed -_Nothing has changed._ - - - -# v1.34.0 - -[Documentation](https://docs.k8s.io) - -## Downloads for v1.34.0 - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes.tar.gz) | `133a1ea99881ac8988b1931908506b8b02e0533c6c6521b67152c00e0ba5c124870a3a5050887827a7d1b1b8cc4b1da9e2b07f76684975585d0947e5d234faa5` -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-src.tar.gz) | `2fa409c71ce0f98dc540baa0e5058f751ee982cf0b9dfe4d3ed5eea1331586e7a464a631909889f9c0758d364643718a336816343136b603ef59bdf43c7a30d7` - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-darwin-amd64.tar.gz) | `20b6c4f9327f4d0b5873429595e2b7bdfec6269e9a39dee69e28ff9f3fd168611f56f378b867c35edc605dac23227b0d95083fdbc676c04f5d8d1142ceff829c` -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-darwin-arm64.tar.gz) | `c48d5efa26f8313f535a173201c38896fa9147fd46a7d3a085c70dcbb16391a894d4c4f09ecb6d1d7ed081a7d3fdd8f71afadd0253a55808addb383680ef89b7` -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-386.tar.gz) | `efc91631134a8cdd543d4e9cf429928b0b7abe2f6212f05ea82ad62830caef74aa4b9b090b45d583912de280e13af87b8b20c0d3fc6fbc43b5c99beb5a9ff8db` -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-amd64.tar.gz) | `aa5e3a41986e23ad6910eb86e68eb10217db60978dadc88370c669cb9c9e10d1431133cc8f7401b4e9843e0d15120c867f2803121e690ac7c74ee85eabbc13b5` -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-arm.tar.gz) | `aeafc3d539a400e2e1a32ed501aca7e265ed817d0d56acf62f306c26c2be0beac6af88b6478a26df865105a2c13f2006cc1e062189f4b6885814133090228e86` -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-arm64.tar.gz) | `24158910deed9d09e99e5fb358bd9758de509f344bfb0b1482b2426e26c1e52f7f97657438fa698b51da10c7444699f7addae58ee67b23f38eb175df0e17661a` -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-ppc64le.tar.gz) | `b30f3966ab6d2b723956cd400e73a685ea6431230eb1994bbb995af163f6ba7abbda79834dab3f0fc0a6b4a9c9af3582f07689e100841ea012015070cac9cd80` -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-linux-s390x.tar.gz) | `b543accac845a9a8d1fccc62e43d44479247f9ed65d7db7e2fcf0004ee02c7eaf9d10ab977040bf77f4f5171974a1d4d8a1852d93668b1f593ad5f957ba84952` -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-windows-386.tar.gz) | `2f60547e2e8800df61c57adfb862031e81ba27cba3edeaf483aa8616820561c6ed9b87778b4e81be14545dcaa35bef9d80c817972039357f8e594a6f4edeeb13` -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-windows-amd64.tar.gz) | `a528fdec4aa426f0b72ff96f39727842e6561f4c49e273e6f007934f42ab2992fd75a8fa43c9ae7d9f3345091228d43bc03e3bdf3696d36a56b4fb49d20a6e9d` -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-client-windows-arm64.tar.gz) | `467dcadaa8b48d45caa0a5aca5669317fd501689e4a90219c701adb5e9f46ce66085dd3800321e2377c775992180d76aae2e2b84a4f7bb50f997198def0dd8e6` - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-server-linux-amd64.tar.gz) | `a9ec9abe6a803d55d56753e1be8549223cd34ebcbec26536cbdc277c5f17a28c4942329e1df01a2bd067b60a0c1c2901e240d5014e9ce445400239bd488582af` -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-server-linux-arm64.tar.gz) | `d05fd68c31f30b1853aa927200ce99fc1e7e67b39803be7508c5591b57e74f3496bcd8b50b84afeabd293f41bc647ea4bcb0bf85a7be5b49e8d2604214e5ccda` -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-server-linux-ppc64le.tar.gz) | `173d638506736cfd0bd8ffe7719447895068ed3f3c8a20405548f0db6689bcd63a4f226f6b19e35e7696801c338d9071f2f93392c8ec6316617303350cb44cff` -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-server-linux-s390x.tar.gz) | `80fd0c55c3c1cdbdd47faf9bfcf2f89d36c56bb91c0281c126e8ba84ad36c527f1861646f54dc4258ba6fae0fb8ee23674ed41f811a08758da3fe1337f723748` - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-node-linux-amd64.tar.gz) | `93ae93af2d39bf00747b66f365781c64880b4ca235031a7ecae7a9d017e04df7ca925f8c005b1da49447cf64cb3f1ecc790db460e60cd1f98f34aae1434ad103` -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-node-linux-arm64.tar.gz) | `33216af73a02919579985be5d5372ecb305b6fb2013297f3ea36b357d3cf4bce2a07a612e188b76c752aabbe23bdc726645f348f5db43b12893fc80ac65711f3` -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-node-linux-ppc64le.tar.gz) | `781df3a7785435ed365949850ef3c4555e3531826907d75e2edf102cdef8950176c17c8dc8ad97077908b12895eb2cf2796e27418252cb790a7876484270d33a` -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-node-linux-s390x.tar.gz) | `133c8c011e3f0c6094262efa2cd053e96facdfdb603f90eb51b9ee085c082ac82bcd53863cc517f7ae9e219265f8e66e94e4fbdc21ee01b79b72c993792dde5c` -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0/kubernetes-node-windows-amd64.tar.gz) | `e5f6dbd19106b4f4d125d048f1351be2b6a06a79622ece31c24a2a27c03268474a42a1b0b85b1de46423a66c0ee9e1060e9bcee709ae1668c7a650b5575ccc76` - -### 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.0](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.0](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.0](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.0](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.0](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.0](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.33.0 - -## Urgent Upgrade Notes - -### (No, really, you MUST read this before you upgrade) - -- For metrics `apiserver_cache_list_fetched_objects_total`, `apiserver_cache_list_returned_objects_total`, `apiserver_cache_list_total` replace `resource_prefix` label with API `group` and `resource` labels. - For metrics `etcd_request_duration_seconds`, `etcd_requests_total` and `etcd_request_errors_total` replace `type` label with API `resource` and `group` label. - For metric `apiserver_selfrequest_total` add a API `group` label. - For metrics `apiserver_watch_events_sizes` and `apiserver_watch_events_total` replace API `kind` label with `resource` label. - For metrics `apiserver_request_body_size_bytes`, `apiserver_storage_events_received_total`, `apiserver_storage_list_evaluated_objects_total`, `apiserver_storage_list_fetched_objects_total`, `apiserver_storage_list_returned_objects_total`, `apiserver_storage_list_total`, `apiserver_watch_cache_events_dispatched_total`, `apiserver_watch_cache_events_received_total`, `apiserver_watch_cache_initializations_total`, `apiserver_watch_cache_resource_version`, `watch_cache_capacity`, `apiserver_init_events_total`, `apiserver_terminated_watchers_total`, `watch_cache_capacity_increase_total`, `watch_cache_capacity_decrease_total`, `apiserver_watch_cache_read_wait_seconds`, `apiserver_watch_cache_consistent_read_total`, `apiserver_storage_consistency_checks_total`, `etcd_bookmark_counts`, `storage_decode_errors_total` extract the API group from `resource` label and put it in new `group` label. ([#131845](https://github.com/kubernetes/kubernetes/pull/131845), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd, Instrumentation and Testing] - - Kubelet: removed the deprecated flag `--cloud-config` from the command line. ([#130161](https://github.com/kubernetes/kubernetes/pull/130161), [@carlory](https://github.com/carlory)) [SIG Cloud Provider, Node and Scalability] - - Static pods that reference API objects were denied admission by the kubelet so that static pods would not be silently running even after the mirror pod creation failed. ([#131837](https://github.com/kubernetes/kubernetes/pull/131837), [@sreeram-venkitesh](https://github.com/sreeram-venkitesh)) [SIG Auth, Node and Testing] - - The Scheduling Framework exposed `NodeInfos` to the PreFilter plugins. The PreFilter plugins now accepted the `NodeInfo` list from the arguments. ([#130720](https://github.com/kubernetes/kubernetes/pull/130720), [@saintube](https://github.com/saintube)) [SIG Node, Scheduling, Storage and Testing] - -## Changes by Kind - -### Deprecation - -- Apimachinery: Deprecated `MessageCountMap` and `CreateAggregateFromMessageCountMap`. ([#132376](https://github.com/kubernetes/kubernetes/pull/132376), [@tico88612](https://github.com/tico88612)) -- DRA kubelet: gRPC API graduated to v1, v1beta1 was deprecated starting in 1.34. Updating DRA drivers to the `k8s.io/dynamic-resource-allocation/kubeletplugin` helper from 1.34 added support for both API versions. ([#132700](https://github.com/kubernetes/kubernetes/pull/132700), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- Deprecated the `preferences` field in kubeconfig in favor of `kuberc`. ([#131741](https://github.com/kubernetes/kubernetes/pull/131741), [@soltysh](https://github.com/soltysh)) [SIG API Machinery, CLI, Cluster Lifecycle and Testing] -- Kubeadm: Consistently prefixed errors with error: when printing them. ([#132080](https://github.com/kubernetes/kubernetes/pull/132080), [@neolit123](https://github.com/neolit123)) -- Kubeadm: Exposed only the non-deprecated klog flags (-v and -vmodule), in line with KEP https://features.k8s.io/2845. ([#131647](https://github.com/kubernetes/kubernetes/pull/131647), [@carsontham](https://github.com/carsontham)) -- [cloud-provider] Respected the `exclude-from-external-load-balancers=false` label. ([#131085](https://github.com/kubernetes/kubernetes/pull/131085), [@kayrus](https://github.com/kayrus)) [SIG Cloud Provider and Network] - -### API Change - -- Added `omitempty` and `opt` tag to the API `v1beta2` AdminAccess type in the `DeviceRequestAllocationResult` struct. ([#132338](https://github.com/kubernetes/kubernetes/pull/132338), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Added a `runtime.ApplyConfiguration` interface implemented by all generated apply configuration types. ([#132194](https://github.com/kubernetes/kubernetes/pull/132194), [@alvaroaleman](https://github.com/alvaroaleman)) [SIG API Machinery and Instrumentation] -- Added a detailed event for in-place pod vertical scaling completed, improving cluster management and debugging. ([#130387](https://github.com/kubernetes/kubernetes/pull/130387), [@shiya0705](https://github.com/shiya0705)) [SIG API Machinery, Apps, Autoscaling, Node, Scheduling and Testing] -- Added a mechanism for configurable container restarts: _container-level restart rules_. This was an alpha feature behind the `ContainerRestartRules` feature gate. ([#132642](https://github.com/kubernetes/kubernetes/pull/132642), [@yuanwang04](https://github.com/yuanwang04)) [SIG API Machinery, Apps, Node and Testing] -- Added a new `FileKeyRef` field to containers, allowing them to load variables from files by setting this field. - - Introduced the `EnvFiles` feature gate to govern activation of this functionality. ([#132626](https://github.com/kubernetes/kubernetes/pull/132626), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG API Machinery, Apps, Node and Testing] -- Added driver-owned fields in `ResourceSlice` to mark whether the device was shareable among multiple resource claims (or requests) and to specify how each capacity could be shared between different requests. - - Added user-owned fields in `ResourceClaim` to specify resource requirements against each device capacity. - - Added scheduler-owned field in `ResourceClaim.Status` to specify how much device capacity is reserved for a specific request. - - Added an additional identifier to `ResourceClaim.Status` for the device supports multiple allocations. - - Added a new constraint type to enforce uniqueness of specified attributes across all allocated devices. ([#132522](https://github.com/kubernetes/kubernetes/pull/132522), [@sunya-ch](https://github.com/sunya-ch)) [SIG API Machinery, Apps, Architecture, CLI, Cluster Lifecycle, Network, Node, Release, Scheduling and Testing] -- Added new optional APIs in `ResouceSlice.Basic` and `ResourceClaim.Status.AllocatedDeviceStatus`. ([#130160](https://github.com/kubernetes/kubernetes/pull/130160), [@KobayashiD27](https://github.com/KobayashiD27)) [SIG API Machinery, Apps, Architecture, Node, Release, Scheduling and Testing] -- Added support for specifying `controlplane` or `cluster` egress selectors in JWT authenticators via the `issuer.egressSelectorType` field in the `AuthenticationConfiguration.jwt` array. If unset, the previous behavior of using no egress selector is preserved. This functionality requires the `StructuredAuthenticationConfigurationEgressSelector` beta feature gate (enabled by default). ([#132768](https://github.com/kubernetes/kubernetes/pull/132768), [@enj](https://github.com/enj)) [SIG API Machinery, Auth and Testing] -- Added support in the Kubelet for monitoring the health of devices allocated via Dynamic Resource Allocation (DRA) and report it in the `pod.status.containerStatuses.allocatedResourcesStatus` field. This required the DRA plugin to implement the new v1alpha1 `NodeHealth` gRPC service. This feature was controlled by the `ResourceHealthStatus` feature gate. ([#130606](https://github.com/kubernetes/kubernetes/pull/130606), [@Jpsassine](https://github.com/Jpsassine)) [SIG Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Network, Node, Release, Scheduling, Storage and Testing] -- Added support in the kubelet's image pull credential tracking for service account-based verification. When an image was pulled using service account credentials via external credential providers, subsequent Pods using the same service account (UID, name, and namespace) could access the cached image without re-authentication for the lifetime of that service account. ([#132771](https://github.com/kubernetes/kubernetes/pull/132771), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] -- Added validation to reject Pods using the `PodLevelResources` feature on Windows OS due to lack of support. The API server rejected Pods with pod-level resources and a `Pod.spec.os.name` targeting Windows. Kubelet on nodes running Windows also rejected Pods with pod-level resources at the admission phase. ([#133046](https://github.com/kubernetes/kubernetes/pull/133046), [@toVersus](https://github.com/toVersus)) [SIG Apps and Node] -- Added warnings when creating headless service with set `loadBalancerIP`,`externalIPs` and/or `SessionAffinity`. ([#132214](https://github.com/kubernetes/kubernetes/pull/132214), [@Peac36](https://github.com/Peac36)) -- Allowed `pvc.spec.VolumeAttributesClassName` to change from non-nil to nil. ([#132106](https://github.com/kubernetes/kubernetes/pull/132106), [@AndrewSirenko](https://github.com/AndrewSirenko)) -- Allowed setting the `hostnameOverride` field in `PodSpec` to specify any RFC 1123 DNS subdomain as the pod's hostname. The `HostnameOverride` feature gate was introduced to control enablement of this functionality. ([#132558](https://github.com/kubernetes/kubernetes/pull/132558), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG API Machinery, Apps, Network, Node and Testing] -- Changed underlying logic for `Eviction Manager` helper functions. ([#132277](https://github.com/kubernetes/kubernetes/pull/132277), [@KevinTMtz](https://github.com/KevinTMtz)) [SIG Node, Scheduling and Testing] -- Changed underlying logic to propagate pod-level hugepage cgroup to containers when they did not specify hugepage resources. - - Added validation to enforce the hugepage aggregated container limits to be smaller than or equal to pod-level limits. This was already enforced with the defaulted requests from the specified limits, however it did not make it clear about both hugepage requests and limits. ([#131089](https://github.com/kubernetes/kubernetes/pull/131089), [@KevinTMtz](https://github.com/KevinTMtz)) [SIG Apps, Node and Testing] -- Corrected the documentation to clarify that `podSelector` is optional and described its default behavior. ([#131354](https://github.com/kubernetes/kubernetes/pull/131354), [@tomoish](https://github.com/tomoish)) -- DRA API: resource.k8s.io/v1alpha3 now only contains DeviceTaintRule. All other types got removed because they became obsolete when introducing the v1beta1 API in 1.32. - before updating a cluster where resourceclaims, resourceclaimtemplates, deviceclasses, or resourceslices might have been stored using Kubernetes < 1.32, delete all of those resources before updating and recreate them as needed while running Kubernetes >= 1.32. ([#132000](https://github.com/kubernetes/kubernetes/pull/132000), [@pohly](https://github.com/pohly)) [SIG Etcd, Node, Scheduling and Testing] -- DRA: Starting with Kubernetes 1.34, the alpha-level `resource.k8s.io/admin-access` label has been updated to `resource.kubernetes.io/admin-access`. Admins using the alpha feature and updating from 1.33 can set both labels, upgrade, then remove `resource.k8s.io/admin-access` when no downgrade is going to happen anymore. ([#131996](https://github.com/kubernetes/kubernetes/pull/131996), [@ritazh](https://github.com/ritazh)) [SIG Node and Testing] -- DRA: The scheduler plugin prevented abnormal filter runtimes by timing out after 10 seconds. This was configurable via the plugin configuration's `FilterTimeout`. Setting it to zero disabled the timeout and restored the behavior of Kubernetes <= 1.33. ([#132033](https://github.com/kubernetes/kubernetes/pull/132033), [@pohly](https://github.com/pohly)) [SIG Node, Scheduling and Testing] -- DRA: When the prioritized list feature was used in a request and the resulting number of allocated devices exceeded the number of allowed devices per claim, the scheduler aborted the attempt to allocate devices early. Previously, it tried to many different combinations, which could take a long time. ([#130593](https://github.com/kubernetes/kubernetes/pull/130593), [@mortent](https://github.com/mortent)) [SIG Apps, Node, Scheduling and Testing] -- DRA: removed support for the v1alpha4 kubelet gRPC API (added in 1.31, superseded in 1.32). DRA drivers using the helper package from Kubernetes >= 1.32 use the v1beta1 API and continue to be supported. ([#132574](https://github.com/kubernetes/kubernetes/pull/132574), [@pohly](https://github.com/pohly)) -- Deprecated `StreamingConnectionIdleTimeout` field of the kubelet config. ([#131992](https://github.com/kubernetes/kubernetes/pull/131992), [@lalitc375](https://github.com/lalitc375)) -- Dynamic Resource Allocation: Graduated core functionality to general availability (GA). This newly stable feature uses the _structured parameters_ flavor of DRA. ([#132706](https://github.com/kubernetes/kubernetes/pull/132706), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Autoscaling, Etcd, Node, Scheduling and Testing] -- Enabled kube-apiserver support for `PodCertificateRequest` and `PodCertificate` projected volumes (behind the `PodCertificateRequest` feature gate). ([#128010](https://github.com/kubernetes/kubernetes/pull/128010), [@ahmedtd](https://github.com/ahmedtd)) [SIG API Machinery, Apps, Auth, Cloud Provider, Etcd, Node, Storage and Testing] -- Extended resources backed by DRA feature allowed cluster operator to specify `extendedResourceName` in `DeviceClass`, and application operator to continue using extended resources in pod's requests to request for DRA devices matching the DeviceClass. - - `NodeResourcesFit` plugin scoring didn't work for extended resources backed by DRA. ([#130653](https://github.com/kubernetes/kubernetes/pull/130653), [@yliaog](https://github.com/yliaog)) [SIG API Machinery, Apps, Auth, Node, Scheduling and Testing] -- Extended the NodePorts scheduling plugin to consider hostPorts used by restartable init containers. ([#132040](https://github.com/kubernetes/kubernetes/pull/132040), [@avrittrohwer](https://github.com/avrittrohwer)) [SIG Scheduling and Testing] -- Fixed a 1.33 regression that causes a nil panic in kube-scheduler when aggregating resource requested across container's spec and status. ([#132895](https://github.com/kubernetes/kubernetes/pull/132895), [@yue9944882](https://github.com/yue9944882)) [SIG Node and Scheduling] -- Fixed prerelease lifecycle for `PodCertificateRequest`. ([#133350](https://github.com/kubernetes/kubernetes/pull/133350), [@carlory](https://github.com/carlory)) -- Introduced OpenAPI format support for `k8s-short-name` and `k8s-long-name` in CustomResourceDefinition schemas. ([#132504](https://github.com/kubernetes/kubernetes/pull/132504), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] -- Introduced the `admissionregistration.k8s.io/v1beta1/MutatingAdmissionPolicy` API type. To enable, enable the `MutatingAdmissionPolicy` feature gate (which was off by default) and set `--runtime-config=admissionregistration.k8s.io/v1beta1=true` on the kube-apiserver. - Note that the default stored version remained alpha in 1.34, and whoever enabled beta during 1.34 needed to run a storage migration yourself to ensure you don't depend on alpha data in etcd. ([#132821](https://github.com/kubernetes/kubernetes/pull/132821), [@cici37](https://github.com/cici37)) [SIG API Machinery, Etcd and Testing] -- Kube-apiserver: Added support for disabling caching of authorization webhook decisions in the `--authorization-config` file. The new fields `cacheAuthorizedRequests` and `cacheUnauthorizedRequests` could be set to `false` to prevent caching for authorized or unauthorized requests. See the https://kubernetes.io/docs/reference/access-authn-authz/authorization/#using-configuration-file-for-authorization for more details. ([#129237](https://github.com/kubernetes/kubernetes/pull/129237), [@rfranzke](https://github.com/rfranzke)) [SIG API Machinery and Auth] -- Kube-apiserver: Promoted the `StructuredAuthenticationConfiguration` feature gate to GA. ([#131916](https://github.com/kubernetes/kubernetes/pull/131916), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-apiserver: the AuthenticationConfiguration type accepted in `--authentication-config` files has been promoted to `apiserver.config.k8s.io/v1`. ([#131752](https://github.com/kubernetes/kubernetes/pull/131752), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-log-runner: Added the `-log-file-size` parameter to rotate log output into a new file once it reached a certain size. Introduced `-log-file-age` to enable automatic removal of old output files, and `-flush-interval` to support periodic flushing. ([#127667](https://github.com/kubernetes/kubernetes/pull/127667), [@zylxjtu](https://github.com/zylxjtu)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Release, Scheduling, Storage, Testing and Windows] -- Kubectl: Graduated kuberc support to beta. A `kuberc` configuration file provided a mechanism for customizing `kubectl` behavior (distinct from kubeconfig, which configures cluster access across different clients). ([#131818](https://github.com/kubernetes/kubernetes/pull/131818), [@soltysh](https://github.com/soltysh)) [SIG CLI and Testing] -- Promoted Job Pod Replacement Policy to general availability. The `JobPodReplacementPolicy` feature gate was locked to `true` and will be removed in a future Kubernetes release. ([#132173](https://github.com/kubernetes/kubernetes/pull/132173), [@dejanzele](https://github.com/dejanzele)) [SIG Apps and Testing] -- Promoted `MutableCSINodeAllocatableCount` to beta. ([#132429](https://github.com/kubernetes/kubernetes/pull/132429), [@torredil](https://github.com/torredil)) -- Promoted feature-gate `VolumeAttributesClass` to GA - - Promoted API `VolumeAttributesClass` and `VolumeAttributesClassList` to `storage.k8s.io/v1`. ([#131549](https://github.com/kubernetes/kubernetes/pull/131549), [@carlory](https://github.com/carlory)) [SIG API Machinery, Apps, Auth, CLI, Etcd, Storage and Testing] -- Promoted the `APIServerTracing` feature gate to GA. The `--tracing-config-file` flag accepted `TracingConfiguration` in version `apiserver.config.k8s.io/v1` (with no changes from `apiserver.config.k8s.io/v1beta1`). ([#132340](https://github.com/kubernetes/kubernetes/pull/132340), [@dashpole](https://github.com/dashpole)) [SIG API Machinery and Testing] -- Promoted the `AuthorizeWithSelectors` and `AuthorizeNodeWithSelectors` feature gates to stable and locked on. ([#132656](https://github.com/kubernetes/kubernetes/pull/132656), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth and Testing] -- Promoted the `KubeletTracing` feature gate to GA. ([#132341](https://github.com/kubernetes/kubernetes/pull/132341), [@dashpole](https://github.com/dashpole)) [SIG Instrumentation and Node] -- Promoted the `RelaxedEnvironmentVariableValidation` feature gate to GA and locked it in the enabled state by default. ([#132054](https://github.com/kubernetes/kubernetes/pull/132054), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Apps, Architecture, Node and Testing] -- Removed an inaccurate statement about requiring ports when the Pod spec `hostNetwork` field was set. ([#130994](https://github.com/kubernetes/kubernetes/pull/130994), [@BenTheElder](https://github.com/BenTheElder)) [SIG Network and Node] -- Removed deprecated `gogo` protocol definitions from `k8s.io/kubelet/pkg/apis/pluginregistration` in favor of `google.golang.org/protobuf`. ([#132773](https://github.com/kubernetes/kubernetes/pull/132773), [@saschagrunert](https://github.com/saschagrunert)) -- Removed deprecated gogo protocol definitions from `k8s.io/cri-api` in favor of `google.golang.org/protobuf`. ([#128653](https://github.com/kubernetes/kubernetes/pull/128653), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery, Auth, Instrumentation, Node and Testing] -- Replaced Boolean-pointer-helper functions with the `k8s.io/utils/ptr` implementations. ([#132794](https://github.com/kubernetes/kubernetes/pull/132794), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Auth, CLI, Node and Testing] -- Replaced `boolPtrFn` helper functions with the "k8s.io/utils/ptr" implementation. ([#132907](https://github.com/kubernetes/kubernetes/pull/132907), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the apiextensions-apiserver apiextensions. ([#132723](https://github.com/kubernetes/kubernetes/pull/132723), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the apiserver (1/2). ([#132751](https://github.com/kubernetes/kubernetes/pull/132751), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the component-base. ([#132754](https://github.com/kubernetes/kubernetes/pull/132754), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Architecture, Instrumentation and Scheduling] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the kube-aggregator apiregistration. ([#132701](https://github.com/kubernetes/kubernetes/pull/132701), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Simplied validation error message for invalid fields by removing redundant field name. ([#132513](https://github.com/kubernetes/kubernetes/pull/132513), [@xiaoweim](https://github.com/xiaoweim)) [SIG API Machinery, Apps, Auth, Node and Scheduling] -- Simplied validation error message for required fields by removing redundant messages. ([#132472](https://github.com/kubernetes/kubernetes/pull/132472), [@xiaoweim](https://github.com/xiaoweim)) [SIG API Machinery, Apps, Architecture, Auth, Cloud Provider, Network, Node and Storage] -- The `KubeletServiceAccountTokenForCredentialProviders` feature was beta and enabled by default. ([#133017](https://github.com/kubernetes/kubernetes/pull/133017), [@aramase](https://github.com/aramase)) [SIG Auth and Node] -- The `conditionType` is "oneof" approved/denied check of CertificateSigningRequest's `.status.conditions` field was migrated to declarative validation. - If the `DeclarativeValidation` feature gate was enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate was enabled, declarative validation was the primary source of errors for migrated fields. ([#133013](https://github.com/kubernetes/kubernetes/pull/133013), [@aaron-prindle](https://github.com/aaron-prindle)) [SIG API Machinery and Auth] -- The fallback behavior of the Downward API's `resourceFieldRef` field was updated to account for pod-level resources: if container-level limits were not set, pod-level limits were now used before falling back to node allocatable resources. ([#132605](https://github.com/kubernetes/kubernetes/pull/132605), [@toVersus](https://github.com/toVersus)) [SIG Node, Scheduling and Testing] -- The validation of `replicas` field in the ReplicationController `/scale` subresource has been migrated to declarative validation. - If the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate is enabled, declarative validation is the primary source of errors for migrated fields. ([#131664](https://github.com/kubernetes/kubernetes/pull/131664), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery and Apps] -- The validation-gen code generator generated validation code that supported validation ratcheting. ([#132236](https://github.com/kubernetes/kubernetes/pull/132236), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps, Auth and Node] -- Updated `IsDNS1123SubdomainWithUnderscore` so that, when it returned an error, it also returned the correct regex information (`dns1123SubdomainFmtWithUnderscore`). ([#132034](https://github.com/kubernetes/kubernetes/pull/132034), [@ChosenFoam](https://github.com/ChosenFoam)) -- Updated etcd version to v3.6.0. ([#131501](https://github.com/kubernetes/kubernetes/pull/131501), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] -- Updated the `v1` credential provider configuration to include the `tokenAttributes.cacheType` field. This field is required and must be set to either `ServiceAccount` or `Token` when configuring a provider that uses a service account to fetch registry credentials. ([#132617](https://github.com/kubernetes/kubernetes/pull/132617), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] -- Zero-value `metadata.creationTimestamp` values are now omitted and no longer serialize an explicit `null` in JSON, YAML, and CBOR output ([#130989](https://github.com/kubernetes/kubernetes/pull/130989), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Scheduling, Storage and Testing] -- `AppArmor` profiles specified in the Pod or container `SecurityContext` were no longer copied to deprecated `AppArmor` annotations (prefix `container.apparmor.security.beta.kubernetes.io/`). Anything that inspected the deprecated annotations must be migrated to use the `SecurityContext` fields instead. ([#131989](https://github.com/kubernetes/kubernetes/pull/131989), [@tallclair](https://github.com/tallclair)) -- `MultiCIDRServiceAllocator` was locked and enabled by default, `DisableAllocatorDualWrite` was enabled by default. ([#131318](https://github.com/kubernetes/kubernetes/pull/131318), [@aojea](https://github.com/aojea)) [SIG API Machinery, Apps, Architecture, Auth, Etcd, Network and Testing] - -### Feature - -- Added 3 new metrics for monitoring async API calls in the scheduler when the `SchedulerAsyncAPICalls` feature gate was enabled: - - `scheduler_async_api_call_execution_total`: tracks executed API calls by call type and result (success/error) - - `scheduler_async_api_call_duration_seconds`: histogram of API call execution duration by call type and result - - `scheduler_pending_async_api_calls`: gauge showing current number of pending API calls in the queue. ([#133120](https://github.com/kubernetes/kubernetes/pull/133120), [@utam0k](https://github.com/utam0k)) [SIG Release and Scheduling] -- Added HPA support to pod-level resource specifications. When the pod-level resource feature was enabled, HPAs configured with `Resource` type metrics calculated the pod resources from `pod.Spec.Resources` field, if specified. ([#132430](https://github.com/kubernetes/kubernetes/pull/132430), [@laoj2](https://github.com/laoj2)) [SIG Apps, Autoscaling and Testing] -- Added Traffic Distribution field to `kubectl describe service` output ([#131491](https://github.com/kubernetes/kubernetes/pull/131491), [@tchap](https://github.com/tchap)) [SIG CLI] -- Added `SizeBasedListCostEstimate` feature gate that allowed apiserver to estimate sizes of objects to calculate cost of LIST requests. ([#132355](https://github.com/kubernetes/kubernetes/pull/132355), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Added `apiserver_resource_size_estimate_bytes` metric to API server. ([#132893](https://github.com/kubernetes/kubernetes/pull/132893), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd and Instrumentation] -- Added `started_user_namespaced_pods_total` and `started_user_namespaced_pods_errors_total` for tracking the successes and failures in creating pods if a user namespace was requested. ([#132902](https://github.com/kubernetes/kubernetes/pull/132902), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] -- Added a `--show-swap` option to `kubectl top` subcommands ([#129458](https://github.com/kubernetes/kubernetes/pull/129458), [@iholder101](https://github.com/iholder101)) [SIG CLI] -- Added a `container_swap_limit_bytes` metric to expose the swap limit assigned to containers under the `LimitedSwap` swap behavior. ([#132348](https://github.com/kubernetes/kubernetes/pull/132348), [@iholder101](https://github.com/iholder101)) [SIG Node and Testing] -- Added a delay to node updates after kubelet startup. A random offset, based on the configured `nodeStatusReportFrequency`, helped distribute traffic and load from node status updates more evenly over time. The initial status update could occur up to 50% earlier or later than the regular schedule. ([#130919](https://github.com/kubernetes/kubernetes/pull/130919), [@mengqiy](https://github.com/mengqiy)) -- Added a flag to kubectl version to detect whether a client/server version mismatch was outside the officially supported range. ([#127365](https://github.com/kubernetes/kubernetes/pull/127365), [@omerap12](https://github.com/omerap12)) -- Added a new `PreBindPreFlight` function to the `PreBindPlugin` interface. All in-tree `PreBind` plugins have been updated to implement `PreBindPreFlight` function. ([#132391](https://github.com/kubernetes/kubernetes/pull/132391), [@sanposhiho](https://github.com/sanposhiho)) [SIG Node, Scheduling, Storage and Testing] -- Added a warning when alpha metrics are used with emulated versions. ([#132276](https://github.com/kubernetes/kubernetes/pull/132276), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery and Architecture] -- Added alpha metrics for compatibility versioning ([#131842](https://github.com/kubernetes/kubernetes/pull/131842), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Architecture, Instrumentation and Scheduling] -- Added configurable flags to kube-apiserver for coordinated leader election. ([#132433](https://github.com/kubernetes/kubernetes/pull/132433), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery and Testing] -- Added machine readable output options (JSON & YAML) to `kubectl api-resources`. ([#132604](https://github.com/kubernetes/kubernetes/pull/132604), [@dharmit](https://github.com/dharmit)) [SIG Apps, CLI and Network] -- Added memory tracking to scheduler performance tests to help detect memory leaks and monitored memory usage patterns while running `scheduler_perf`. ([#132910](https://github.com/kubernetes/kubernetes/pull/132910), [@utam0k](https://github.com/utam0k)) [SIG Scheduling and Testing] -- Added support for CEL expressions with escaped names in the structured authentication config. Using `[...]` to access claims or user data was recommended when names contained characters that would otherwise need escaping. CEL optionals with `?` could be used where has was not applicable — for example, `claims[?"kubernetes.io"]` or `user.extra[?"domain.io/foo"]`. ([#131574](https://github.com/kubernetes/kubernetes/pull/131574), [@enj](https://github.com/enj)) [SIG API Machinery and Auth] -- Added support for `--cpu`, `--memory` flag to `kubectl autoscale`, started deprecating `--cpu-precent`. ([#129373](https://github.com/kubernetes/kubernetes/pull/129373), [@googs1025](https://github.com/googs1025)) -- Added support for a new kubectl output format, `kyaml`. KYAML was a strict subset of YAML and should be accepted by any YAML processor. The formatting of KYAML was halfway between JSON and YAML. Because it was more explicit than the default YAML style, it was less error-prone. ([#132942](https://github.com/kubernetes/kubernetes/pull/132942), [@thockin](https://github.com/thockin)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Contributor Experience, Instrumentation, Network, Node, Scheduling, Storage and Testing] -- Added the `DetectCacheInconsistency` feature gate, allowing the API server to periodically verify consistency between its `cache` and `etcd`. Detected inconsistencies reported via the `apiserver_storage_consistency_checks_total` metric and trigger purging of affected cache snapshots. ([#132884](https://github.com/kubernetes/kubernetes/pull/132884), [@serathius](https://github.com/serathius)) [SIG API Machinery, Instrumentation and Testing] -- Added the `SizeBasedListCostEstimate` feature gate (enabled by default), which changes how APF seats are assigned to `LIST` requests. With this feature, one seat is assigned per 100KB of data loaded into memory at once during a `LIST` operation. ([#132932](https://github.com/kubernetes/kubernetes/pull/132932), [@serathius](https://github.com/serathius)) -- Added useful endpoints for kube-apiserver. ([#132581](https://github.com/kubernetes/kubernetes/pull/132581), [@itssimrank](https://github.com/itssimrank)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] -- Built Kubernetes using Go 1.24.3. ([#131934](https://github.com/kubernetes/kubernetes/pull/131934), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- Built Kubernetes using Go 1.24.4. ([#132222](https://github.com/kubernetes/kubernetes/pull/132222), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- Bumped DRA API version to `v1` in `deviceattribute` package in `k8s.io/dynamic-resource-allocation`. ([#133164](https://github.com/kubernetes/kubernetes/pull/133164), [@everpeace](https://github.com/everpeace)) -- Bumped `KubeletCgroupDriverFromCRI` to GA and add metric to track out-of-support CRI implementations. ([#133157](https://github.com/kubernetes/kubernetes/pull/133157), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] -- CRI API had auth fields in image pulling marked as `debug_redact`. ([#133135](https://github.com/kubernetes/kubernetes/pull/133135), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) -- Changed handling of `CustomResourceDefinitions` with unrecognized formats. Writing a schema with an unrecognized format now triggered a warning (the write was still accepted). ([#133136](https://github.com/kubernetes/kubernetes/pull/133136), [@yongruilin](https://github.com/yongruilin)) -- DRA kubelet: Fixed the kubelet to also clean up `ResourceSlices` in some additional failure scenarios (driver was removed forcibly or crashed and did not restart). ([#132058](https://github.com/kubernetes/kubernetes/pull/132058), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- DRAAdminAccess was enabled by default allowing users to create `ResourceClaims` and `ResourceClaimTemplates` in privileged mode to grant access to devices that were in use by other users for admin tasks like monitoring health or status of the device. ([#133085](https://github.com/kubernetes/kubernetes/pull/133085), [@ritazh](https://github.com/ritazh)) [SIG Auth and Node] -- Demoted KEP-5278 feature gates `ClearingNominatedNodeNameAfterBinding` and `NominatedNodeNameForExpectation` to alpha from beta. ([#133293](https://github.com/kubernetes/kubernetes/pull/133293), [@utam0k](https://github.com/utam0k)) [SIG Scheduling and Testing] -- Deprecated `apiserver_storage_objects` and replaced it with `apiserver_resource_objects` metric using labels consistent with other metrics. ([#132965](https://github.com/kubernetes/kubernetes/pull/132965), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd and Instrumentation] -- Eliminated work when creating Services or understanding port purposes, especially for external resources deployed via Helm charts. ([#133018](https://github.com/kubernetes/kubernetes/pull/133018), [@rushmash91](https://github.com/rushmash91)) -- Enabled compact snapshots in the watch cache based on `etcd` compaction events. ([#132876](https://github.com/kubernetes/kubernetes/pull/132876), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Enabled completion for aliases defined in `kubectlrc`. ([#131586](https://github.com/kubernetes/kubernetes/pull/131586), [@ardaguclu](https://github.com/ardaguclu)) -- Ensured memory resizing for Guaranteed QoS pods on static Memory policy configurations was gated by `InPlacePodVerticalScalingExclusiveMemory` (defaults: `false`). ([#132473](https://github.com/kubernetes/kubernetes/pull/132473), [@pravk03](https://github.com/pravk03)) [SIG Node, Scheduling and Testing] -- Ensured that non-scheduling related errors (e.g., network errors) did not lengthen the Pod scheduling backoff time. ([#128748](https://github.com/kubernetes/kubernetes/pull/128748), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Executed API calls dispatched during pod scheduling asynchronously if the `SchedulerAsyncAPICalls` feature gate was enabled. - Out-of-tree plugins used `APIDispatcher` and `APICacher` from the framework to dispatch their own calls. ([#132886](https://github.com/kubernetes/kubernetes/pull/132886), [@macsko](https://github.com/macsko)) [SIG Release, Scheduling and Testing] -- Fixed recording the `kubelet_container_resize_requests_total` metric to include all resize-related updates. ([#133060](https://github.com/kubernetes/kubernetes/pull/133060), [@natasha41575](https://github.com/natasha41575)) -- Graduated `ListFromCacheSnapshot` to beta. ([#132901](https://github.com/kubernetes/kubernetes/pull/132901), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Graduated `PodLevelResources` feature to beta and have it on by default. This feature allowed defining CPU and memory resources for an entire pod in `pod.spec.resources`. ([#132999](https://github.com/kubernetes/kubernetes/pull/132999), [@ndixita](https://github.com/ndixita)) -- Graduated `PodObservedGenerationTracking` feature to beta and had it on by default. This feature meant that the top level `status.observedGeneration` and `status.conditions[].observedGeneration` fields in Pods were populated to reflect the `metadata.generation` of the podspec at the time that the status or condition was reported. ([#132912](https://github.com/kubernetes/kubernetes/pull/132912), [@natasha41575](https://github.com/natasha41575)) [SIG Apps, Node and Testing] -- Graduated `ResilientWatchCacheInitialization` to GA. ([#131979](https://github.com/kubernetes/kubernetes/pull/131979), [@serathius](https://github.com/serathius)) -- Graduated `StreamingCollectionEncodingToJSON` and `StreamingCollectionEncodingToProtobuf` to GA. ([#132648](https://github.com/kubernetes/kubernetes/pull/132648), [@serathius](https://github.com/serathius)) -- Graduated configurable endpoints for anonymous authentication using the authentication configuration file to stable. ([#131654](https://github.com/kubernetes/kubernetes/pull/131654), [@vinayakankugoyal](https://github.com/vinayakankugoyal)) [SIG API Machinery and Testing] -- Graduated relaxed DNS search string validation to GA. For the Pod API, `.spec.dnsConfig.searches` - now allows an underscore (`_`) where a dash (`-`) would be allowed, and it allows search strings be a single dot `.`. ([#132036](https://github.com/kubernetes/kubernetes/pull/132036), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Network and Testing] -- Graduated scheduler `QueueingHint` support to GA (general availability) ([#131973](https://github.com/kubernetes/kubernetes/pull/131973), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Graduated the WinOverlay feature in the kube-proxy to GA. The **WinOverlay** feature gate was enabled by default. ([#133042](https://github.com/kubernetes/kubernetes/pull/133042), [@rzlink](https://github.com/rzlink)) [SIG Network and Windows] -- Graduated the `ConsistentListFromCache` to GA. ([#132645](https://github.com/kubernetes/kubernetes/pull/132645), [@serathius](https://github.com/serathius)) -- Graduated the `WatchList` feature gate to beta for kube-apiserver and enabled `WatchListClient` for KCM. ([#132704](https://github.com/kubernetes/kubernetes/pull/132704), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- Graduated the `WinDSR` feature in the kube-proxy to GA. The `WinDSR` feature gate was enabled by default. ([#132108](https://github.com/kubernetes/kubernetes/pull/132108), [@rzlink](https://github.com/rzlink)) [SIG Network and Windows] -- If `PreBindPreFlight` returned `Skip`, the scheduler didn't run the plugin at `PreBind`. - If any `PreBindPreFlight` returned `Success`, the scheduler put NominatedNodeName to the pod - so that other components (such as the cluster autoscaler) could notice the pod was going to be bound to the node. ([#133021](https://github.com/kubernetes/kubernetes/pull/133021), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Implemented prioritization of resize requests based on `priorityClass` and QoS class when node resources are insufficient to accommodate all pending resize operations. ([#132342](https://github.com/kubernetes/kubernetes/pull/132342), [@natasha41575](https://github.com/natasha41575)) [SIG Node and Testing] -- Included the namespace in the output of `kubectl delete` for better identification of resources. ([#126619](https://github.com/kubernetes/kubernetes/pull/126619), [@totegamma](https://github.com/totegamma)) -- Increased APF max seats to 100 for LIST requests. ([#133034](https://github.com/kubernetes/kubernetes/pull/133034), [@serathius](https://github.com/serathius)) -- Introduced a method `GetPCIeRootAttributeByPCIBusID(pciBusID)` for third-party DRA drivers to provide common logic for the standardized device attribute `resource.kubernetes.io/pcieRoot`. ([#132296](https://github.com/kubernetes/kubernetes/pull/132296), [@everpeace](https://github.com/everpeace)) -- Kube-apiserver reported the last configuration hash as a label in - - - `apiserver_authentication_config_controller_last_config_info` metric after successfully loading the authentication configuration file. - - `apiserver_authorization_config_controller_last_config_info` metric after successfully loading the authorization configuration file. - - `apiserver_encryption_config_controller_last_config_info` metric after successfully loading the encryption configuration file. ([#132299](https://github.com/kubernetes/kubernetes/pull/132299), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-apiserver: Each unique set of etcd server overrides specified with `--etcd-servers-overrides` surfaced health checks named `etcd-override-` and `etcd-override-readiness-`. These checks were still excluded by the `?exclude=etcd` and `?exclude=etcd-readiness` directives. ([#129438](https://github.com/kubernetes/kubernetes/pull/129438), [@pacoxu](https://github.com/pacoxu)) [SIG API Machinery and Testing] -- Kube-apiserver: Previously persisted `CustomResourceDefinition` objects with an invalid whitespace-only `caBundle` could serve requests that did not require conversion. ([#132514](https://github.com/kubernetes/kubernetes/pull/132514), [@tiffanny29631](https://github.com/tiffanny29631)) -- Kube-apiserver: Promoted the `ExternalServiceAccountTokenSigner` feature to beta, which enabled external signing of service account tokens and fetching of public verifying keys. This was accomplished by enabling the beta `ExternalServiceAccountTokenSigner` feature gate and specifying the `--service-account-signing-endpoint` flag. The flag value could either be the path to a Unix domain socket on the filesystem, or be prefixed with @ to indicate a Unix domain socket in the abstract namespace. ([#131300](https://github.com/kubernetes/kubernetes/pull/131300), [@HarshalNeelkamal](https://github.com/HarshalNeelkamal)) [SIG API Machinery, Auth and Testing] -- Kube-proxy: Checked whether IPv6 was available on Linux before using it. ([#131265](https://github.com/kubernetes/kubernetes/pull/131265), [@rikatz](https://github.com/rikatz)) -- Kubeadm: Added support for ECDSA-P384 as an encryption algorithm type in v1beta4. ([#131677](https://github.com/kubernetes/kubernetes/pull/131677), [@lalitc375](https://github.com/lalitc375)) -- Kubeadm: Fixed an issue where etcd member promotion failed with an error indicating the member was already promoted. ([#130782](https://github.com/kubernetes/kubernetes/pull/130782), [@BernardMC](https://github.com/BernardMC)) -- Kubeadm: graduated the `NodeLocalCRISocket` feature gate to beta and enabed it by default. When its enabled, kubeadm will: - 1. Generate a `/var/lib/kubelet/instance-config.yaml` file to customize the `containerRuntimeEndpoint` field in per-node kubelet configurations. - 2. Remove the `kubeadm.alpha.kubernetes.io/cri-socket` annotation from nodes during upgrade operations. - 3. Remove the `--container-runtime-endpoint` flag from the `/var/lib/kubelet/kubeadm-flags.env` file during upgrades. ([#131981](https://github.com/kubernetes/kubernetes/pull/131981), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Cluster Lifecycle] -- Kubeadm: graduated the kubeadm specific feature gate `WaitForAllControlPlaneComponents` to GA. The feature gate is was locked to always be enabled and on node initialization kubeadm performed a health check for all control plane components and not only the `kube-apiserver`. ([#132594](https://github.com/kubernetes/kubernetes/pull/132594), [@neolit123](https://github.com/neolit123)) -- Kubeadm: switched the validation check for Linux kernel version to throw warnings instead of errors. ([#131919](https://github.com/kubernetes/kubernetes/pull/131919), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle and Node] -- Kubelet detected terminal CSI volume mount failures due to exceeded attachment limits on the node and marked the Stateful Pod as Failed, allowing its controller to recreate it. This prevented Pods from getting stuck indefinitely in the `ContainerCreating` state. ([#132933](https://github.com/kubernetes/kubernetes/pull/132933), [@torredil](https://github.com/torredil)) [SIG Apps, Node, Storage and Testing] -- Kubelet reported a hash of the credential provider configuration via the `kubelet_credential_provider_config_info` metric. The hash was exposed in the `hash` label. ([#133016](https://github.com/kubernetes/kubernetes/pull/133016), [@aramase](https://github.com/aramase)) [SIG API Machinery and Auth] -- Kubelet: Extended the `--image-credential-provider-config` flag to accept a directory path in addition to a single file. When a directory was specified, all .json, .yaml, and .yml files in that directory were loaded and merged in lexicographical order. ([#131658](https://github.com/kubernetes/kubernetes/pull/131658), [@dims](https://github.com/dims)) [SIG Auth and Node] -- LeaseLocks could now have custom labels that different holders would overwrite when they became the holder of the underlying lease. ([#131632](https://github.com/kubernetes/kubernetes/pull/131632), [@DerekFrank](https://github.com/DerekFrank)) -- Memory limits could be decreased with a `NotRequired` resize restart policy. When decreasing memory limits,a best-effort check was performed to prevent limits from decreasing below usage and triggering an OOM-kill. ([#133012](https://github.com/kubernetes/kubernetes/pull/133012), [@tallclair](https://github.com/tallclair)) [SIG Apps, Node and Testing] -- Migrated validation in `CertificateSigningRequest` to use declarative validation. When the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. If `DeclarativeValidationTakeover` is enabled, declarative validation becomes the primary source of errors for migrated fields. ([#132361](https://github.com/kubernetes/kubernetes/pull/132361), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery and Auth] -- Moved Recover from volume expansion failure to GA. ([#132662](https://github.com/kubernetes/kubernetes/pull/132662), [@gnufied](https://github.com/gnufied)) [SIG Apps, Auth, Node, Storage and Testing] -- Prevented any type of CPU/Memory alignment or hint generation with the Topology Manager from the CPU or Memory Manager when pod-level resources were used in the Pod spec. ([#133279](https://github.com/kubernetes/kubernetes/pull/133279), [@ffromani](https://github.com/ffromani)) [SIG Node and Testing] -- Promoted Linux node pressure stall information (PSI) metrics to beta. ([#132822](https://github.com/kubernetes/kubernetes/pull/132822), [@roycaihw](https://github.com/roycaihw)) [SIG Node] -- Promoted Windows graceful shutdown feature from alpha to beta. ([#133062](https://github.com/kubernetes/kubernetes/pull/133062), [@zylxjtu](https://github.com/zylxjtu)) -- Promoted the Ordered Namespace Deletion test to Conformance. ([#132219](https://github.com/kubernetes/kubernetes/pull/132219), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture and Testing] -- Promoted the `KubeletPodResourcesDynamicResources` and `KubeletPodResourcesGet` feature gates to beta, which were enabled by default if DRA went to GA. ([#132940](https://github.com/kubernetes/kubernetes/pull/132940), [@guptaNswati](https://github.com/guptaNswati)) -- Promoted the feature `OrderedNamespaceDeletion` to GA. ([#131514](https://github.com/kubernetes/kubernetes/pull/131514), [@cici37](https://github.com/cici37)) [SIG API Machinery and Testing] -- Removed "endpoint-controller" and "workload-leader-election" FlowSchemas from the default APF configuration. - - migrate the lock type used in the leader election in your workloads from configmapsleases/endpointsleases to leases. ([#131215](https://github.com/kubernetes/kubernetes/pull/131215), [@tosi3k](https://github.com/tosi3k)) [SIG API Machinery, Apps, Network, Scalability and Scheduling] -- Started recording metrics for in-place Pod resize. ([#132903](https://github.com/kubernetes/kubernetes/pull/132903), [@natasha41575](https://github.com/natasha41575)) -- The Kubernetes API server merged selectors built from `matchLabelKeys` into the `labelSelector` of `topologySpreadConstraints`, aligning Pod Topology Spread behavior with Inter-Pod Affinity. To prevent breaking existing Pods using `matchLabelKeys`, this scheduler behavior was preserved until v1.34. Upgrades from v1.32 to v1.34 should be done incrementally (v1.32 → v1.33 → v1.34), ensuring Pods created at v1.32 with `matchLabelKeys` are scheduled before reaching v1.34. Controllers relying on `matchLabelKeys` no longer need to handle them directly and can use `labelSelector` instead. The new feature gate `MatchLabelKeysInPodTopologySpreadSelectorMerge`, enabled by default, controls this behavior. ([#129874](https://github.com/kubernetes/kubernetes/pull/129874), [@mochizuki875](https://github.com/mochizuki875)) [SIG Apps, Node, Scheduling and Testing] -- The PreferSameTrafficDistribution feature gate is now enabled by default, - enabling the `PreferSameNode` traffic distribution value for Services. ([#132127](https://github.com/kubernetes/kubernetes/pull/132127), [@danwinship](https://github.com/danwinship)) [SIG Apps and Network] -- The new `dra_resource_claims_in_use` kubelet metrics reported active `ResourceClaims`, overall and by driver. ([#131641](https://github.com/kubernetes/kubernetes/pull/131641), [@pohly](https://github.com/pohly)) [SIG Architecture, Instrumentation, Node and Testing] -- The scheduler no longer cleared the `nominatedNodeName` field for Pods. External components, such as Cluster Autoscaler and Karpenter, were responsible for managing this field when needed. ([#133276](https://github.com/kubernetes/kubernetes/pull/133276), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] -- The validation in the CertificateSigningRequest `/status` and `/approval` subresources was migrated to declarative validation. - If the `DeclarativeValidation` feature gate was enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate was enabled, declarative validation was the primary source of errors for migrated fields. ([#133068](https://github.com/kubernetes/kubernetes/pull/133068), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery and Auth] -- Updated `kube-controller-manager` events to support contextual logging. ([#128351](https://github.com/kubernetes/kubernetes/pull/128351), [@mengjiao-liu](https://github.com/mengjiao-liu)) -- Updated pause version to `registry.k8s.io/pause:3.10.1`. ([#130713](https://github.com/kubernetes/kubernetes/pull/130713), [@ArkaSaha30](https://github.com/ArkaSaha30)) [SIG Cluster Lifecycle, Node, Scheduling and Testing] -- Updated the Kubernetes build environment to use Go `1.24.5`. ([#132896](https://github.com/kubernetes/kubernetes/pull/132896), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- Updated the built in `system:monitoring` role with permission to access kubelet metrics endpoints. ([#132178](https://github.com/kubernetes/kubernetes/pull/132178), [@gavinkflam](https://github.com/gavinkflam)) [SIG Auth] -- When `RelaxedServiceNameValidation` feature gate is enabled, the - names of new Services names are validation with `NameIsDNSLabel()`, - relaxing the pre-existing validation. ([#132339](https://github.com/kubernetes/kubernetes/pull/132339), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Apps, Network and Testing] -- When proxying to an aggregated API server, kube-apiserver used the - `EndpointSlices` of the `service` indicated by the `APIServer`, rather than - using Endpoints. - - If you were using the aggregated API server feature, and you were writing out - the endpoints for it by hand (rather than letting kube-controller-manager - generate `Endpoints` and `EndpointSlices` for it automatically based on the - Service definition), then you should write out an EndpointSlice object rather - than (or in addition to) an `Endpoints` object. ([#129837](https://github.com/kubernetes/kubernetes/pull/129837), [@danwinship](https://github.com/danwinship)) [SIG API Machinery, Network and Testing] -- Whenever a pod was successfully bound to a node, the kube-apiserver cleared the pod's `nominatedNodeName` field. This prevented stale information from affecting external scheduling components. ([#132443](https://github.com/kubernetes/kubernetes/pull/132443), [@utam0k](https://github.com/utam0k)) [SIG Apps, Node, Scheduling and Testing] -- `DRAPrioritizedList` was turned on by default which made it possible to provide a prioritized list of subrequests in a `ResourceClaim`. ([#132767](https://github.com/kubernetes/kubernetes/pull/132767), [@mortent](https://github.com/mortent)) [SIG Node, Scheduling and Testing] -- `PodLifecycleSleepAction` was graduated to GA. ([#132595](https://github.com/kubernetes/kubernetes/pull/132595), [@AxeZhan](https://github.com/AxeZhan)) [SIG Apps, Node and Testing] -- `kube-controller-manager` reported the following metrics for `ResourceClaims` with admin access: - - `resourceclaim_controller_creates_total` count metric with labels `admin_access` (true or false), `status` (failure or success) to track the total number of `ResourceClaims` creation requests - - `resourceclaim_controller_resource_claims` gauge metric with labels `admin_access` (true or false), `allocated` (true or false) to track the current number of `ResourceClaims`. ([#132800](https://github.com/kubernetes/kubernetes/pull/132800), [@ritazh](https://github.com/ritazh)) [SIG Apps, Auth, Instrumentation and Node] -- `kubeadm`: Started using a named port `probe-port` for all probes in the static pod manifests generated by `kubeadm` for the `kube-apiserver`, `kube-controller-manager`, `kube-scheduler`, and related components. If probe port values were previously patched using `kubeadm` patches, the corresponding named port under the container’s `ports` field must now also be patched. ([#132776](https://github.com/kubernetes/kubernetes/pull/132776), [@neolit123](https://github.com/neolit123)) - -### Failing Test - -- DRA driver helper: Fixed handling of apiserver restart when running on a Kubernetes version which did not support the `resource.k8s.io` version used by the DRA driver. ([#133076](https://github.com/kubernetes/kubernetes/pull/133076), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- Fixed e2e test "[Driver: csi-hostpath] [Testpattern: Dynamic PV (filesystem volmode)] volumeLimits should support volume limits" not to leak Pods and namespaces. ([#132674](https://github.com/kubernetes/kubernetes/pull/132674), [@jsafrane](https://github.com/jsafrane)) [SIG Storage and Testing] -- Kube-apiserver: The --service-account-signing-endpoint flag now only validates the format of abstract socket names ([#131509](https://github.com/kubernetes/kubernetes/pull/131509), [@liggitt](https://github.com/liggitt)) [SIG API Machinery and Auth] - -### Bug or Regression - -- Added `podSpec` validation for creating `StatefulSet`. ([#131790](https://github.com/kubernetes/kubernetes/pull/131790), [@chengjoey](https://github.com/chengjoey)) [SIG Apps, Etcd and Testing] -- Checked for newer resize fields when deciding the recovery feature status in the kubelet. ([#131418](https://github.com/kubernetes/kubernetes/pull/131418), [@gnufied](https://github.com/gnufied)) -- Clarified help message of `--ignore-not-found` flag. Supported `--ignore-not-found` in `watch` operation. ([#132542](https://github.com/kubernetes/kubernetes/pull/132542), [@gemmahou](https://github.com/gemmahou)) -- DRA drivers: the resource slice controller sometimes didn't react properly when kubelet or someone else deleted a recently created ResourceSlice. It incorrectly assumed that the ResourceSlice still exists and didn't recreate it. ([#132683](https://github.com/kubernetes/kubernetes/pull/132683), [@pohly](https://github.com/pohly)) [SIG Apps, Node and Testing] -- DRA: Ensured that ResourceClaims requesting a fixed number of devices with `adminAccess` were no longer allocated the same device multiple times. ([#131299](https://github.com/kubernetes/kubernetes/pull/131299), [@nojnhuh](https://github.com/nojnhuh)) -- Disabled reading of disk geometry before calling expansion for ext and xfs filesystems. ([#131568](https://github.com/kubernetes/kubernetes/pull/131568), [@gnufied](https://github.com/gnufied)) -- Ensured objects are transformed prior to storage in `SharedInformers` if a transformer is provided and `WatchList` is activated. ([#131799](https://github.com/kubernetes/kubernetes/pull/131799), [@valerian-roche](https://github.com/valerian-roche)) -- Fixed API response for `StorageClassList` queries to return a graceful error message, if the provided `ResourceVersion` is too large. ([#132374](https://github.com/kubernetes/kubernetes/pull/132374), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Etcd] -- Fixed ReplicationController reconciliation when the `DeploymentReplicaSetTerminatingReplicas` feature gate was enabled. ([#131822](https://github.com/kubernetes/kubernetes/pull/131822), [@atiratree](https://github.com/atiratree)) -- Fixed a bug in CEL's common.UnstructuredToVal where `==` evaluates to false for identical objects when a field is present but the value is null. This bug does not impact the Kubernetes API. ([#131559](https://github.com/kubernetes/kubernetes/pull/131559), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery] -- Fixed a bug in the Job controller that could result in creating unnecessary Pods for Jobs already marked as finished (either successful or failed). ([#130333](https://github.com/kubernetes/kubernetes/pull/130333), [@kmala](https://github.com/kmala)) [SIG Apps and Testing] -- Fixed a bug that caused an unexpected delay in creating Pods for newly created Jobs. ([#132109](https://github.com/kubernetes/kubernetes/pull/132109), [@linxiulei](https://github.com/linxiulei)) [SIG Apps and Testing] -- Fixed a bug that caused duplicate validation when updating a ReplicaSet. ([#131873](https://github.com/kubernetes/kubernetes/pull/131873), [@gavinkflam](https://github.com/gavinkflam)) [SIG Apps] -- Fixed a bug that fails to create a replica set when a deployment name is too long. ([#132560](https://github.com/kubernetes/kubernetes/pull/132560), [@hdp617](https://github.com/hdp617)) [SIG API Machinery and Apps] -- Fixed a bug that the async preemption feature keeps preemptor pods unnecessarily in the queue. ([#133167](https://github.com/kubernetes/kubernetes/pull/133167), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling] -- Fixed a panic issue related to kubectl revision history kubernetes/kubectl#1724 ([#130503](https://github.com/kubernetes/kubernetes/pull/130503), [@tahacodes](https://github.com/tahacodes)) [SIG CLI] -- Fixed a possible deadlock in the watch client that could happen if the watch was not stopped. ([#131266](https://github.com/kubernetes/kubernetes/pull/131266), [@karlkfi](https://github.com/karlkfi)) [SIG API Machinery] -- Fixed a regression introduced in 1.33 where some paginated LIST calls fell back to `etcd` instead of being served from cache. ([#132244](https://github.com/kubernetes/kubernetes/pull/132244), [@hakuna-matatah](https://github.com/hakuna-matatah)) -- Fixed an incorrect reference to `JoinConfigurationKind` in the error message when no ResetConfiguration is found during `kubeadm reset` with the `--config` flag. ([#132258](https://github.com/kubernetes/kubernetes/pull/132258), [@J3m3](https://github.com/J3m3)) [SIG Cluster Lifecycle] -- Fixed an issue that allowed Custom Resources to be created using Server-Side Apply even when their `CustomResourceDefinition` was terminating. ([#132467](https://github.com/kubernetes/kubernetes/pull/132467), [@sdowell](https://github.com/sdowell)) -- Fixed an issue where Windows kube-proxy’s `ModifyLoadBalancer` API updates did not match the HNS state in version 15.4. Support for `ModifyLoadBalancer` policy began with Kubernetes 1.31+. ([#131506](https://github.com/kubernetes/kubernetes/pull/131506), [@princepereira](https://github.com/princepereira)) -- Fixed an issue where `insufficientResources` was logged as a pointer during pod preemption, making logs more readable. ([#132183](https://github.com/kubernetes/kubernetes/pull/132183), [@chrisy-x](https://github.com/chrisy-x)) [SIG Node] -- Fixed an issue where the kubelet token cache returned stale tokens when service accounts were recreated with the same name. The cache is now UID-aware. Additionally, the new `TokenRequestServiceAccountUIDValidation` feature gate (Beta, enabled by default) ensures the `TokenRequest` UID matches the service account UID when set. ([#132803](https://github.com/kubernetes/kubernetes/pull/132803), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth, Node and Testing] -- Fixed bug that prevented the alpha feature `PodTopologyLabelAdmission` from working due to checking for the incorrect label key when copying topology labels. This bug delayed the graduation of the feature to beta by an additional release to allow time for meaningful feedback. ([#132462](https://github.com/kubernetes/kubernetes/pull/132462), [@munnerz](https://github.com/munnerz)) -- Fixed incorrect behavior for AllocationMode: All in ResourceClaim when used in subrequests. ([#131660](https://github.com/kubernetes/kubernetes/pull/131660), [@mortent](https://github.com/mortent)) [SIG Node] -- Fixed misleading response codes in admission control metrics. ([#132165](https://github.com/kubernetes/kubernetes/pull/132165), [@gavinkflam](https://github.com/gavinkflam)) [SIG API Machinery, Architecture and Instrumentation] -- Fixed runtime cost estimation for `x-int-or-string` custom resource schemas with maximum lengths. ([#132837](https://github.com/kubernetes/kubernetes/pull/132837), [@JoelSpeed](https://github.com/JoelSpeed)) -- Fixed the `allocatedResourceStatuses` field name mismatch in PVC status validation. ([#131213](https://github.com/kubernetes/kubernetes/pull/131213), [@carlory](https://github.com/carlory)) -- Fixed the `observedGeneration` field in pod resize conditions to accurately reflect the associated pod generation when both `InPlacePodVerticalScaling` and `PodObservedGenerationTracking` feature gates are enabled. ([#131157](https://github.com/kubernetes/kubernetes/pull/131157), [@natasha41575](https://github.com/natasha41575)) -- Fixed the bug when swap related metrics were not available in `/metrics/resource` endpoint. ([#132065](https://github.com/kubernetes/kubernetes/pull/132065), [@yuanwang04](https://github.com/yuanwang04)) [SIG Node and Testing] -- Fixed the problem of validation error when specifying resource requirements at the container level for a resource not supported at the pod level. It implicitly interpreted the pod-level value as 0. ([#132551](https://github.com/kubernetes/kubernetes/pull/132551), [@chao-liang](https://github.com/chao-liang)) [SIG Apps] -- Fixed validation for Job with `suspend=true`, and `completions=0` to set the Complete condition. ([#132614](https://github.com/kubernetes/kubernetes/pull/132614), [@mimowo](https://github.com/mimowo)) [SIG Apps and Testing] -- HPA status displayed memory metrics using Ki. ([#132351](https://github.com/kubernetes/kubernetes/pull/132351), [@googs1025](https://github.com/googs1025)) [SIG Apps and Autoscaling] -- Improved the error message shown when a Pod using user namespaces was created on a runtime that did not support user namespaces. ([#131623](https://github.com/kubernetes/kubernetes/pull/131623), [@rata](https://github.com/rata)) -- Kube-apiserver: Defaulted empty `spec.jobTemplate.spec.podFailurePolicy.rules[*].onPodConditions[*].status` fields for CronJob objects as documented, avoiding validation failures during write requests. ([#131525](https://github.com/kubernetes/kubernetes/pull/131525), [@carlory](https://github.com/carlory)) -- Kube-apiserver: Fixed OIDC discovery document publishing when external service account token signing was enabled. ([#131493](https://github.com/kubernetes/kubernetes/pull/131493), [@hoskeri](https://github.com/hoskeri)) [SIG API Machinery, Auth and Testing] -- Kube-proxy: Removed the iptables CLI wait interval flag. ([#131961](https://github.com/kubernetes/kubernetes/pull/131961), [@cyclinder](https://github.com/cyclinder)) -- Kube-scheduler: in Kubernetes 1.33, the number of devices that can be allocated per ResourceClaim was accidentally reduced to 16. Now the supported number of devices per ResourceClaim is 32 again. ([#131662](https://github.com/kubernetes/kubernetes/pull/131662), [@mortent](https://github.com/mortent)) [SIG Node] -- Kubeadm: Fixed a bug where the default args for etcd were not correct when a local etcd image was used and the etcd version was less than 3.6.0. ([#133023](https://github.com/kubernetes/kubernetes/pull/133023), [@carlory](https://github.com/carlory)) -- Kubelet: Closed a loophole that allowed static Pods to reference arbitrary ResourceClaims. Even though these Pods failed to run due to a sanity check, such references are now explicitly disallowed. ([#131844](https://github.com/kubernetes/kubernetes/pull/131844), [@pohly](https://github.com/pohly)) [SIG Apps, Auth and Node] -- Kubelet: Fixed a bug that caused an unexpected `NodeResizeError` condition to appear in the PVC status when the CSI driver did not support node volume expansion and the PVC had the `ReadWriteMany` access mode. ([#131495](https://github.com/kubernetes/kubernetes/pull/131495), [@carlory](https://github.com/carlory)) -- Modified the node-local `podresources` API endpoint to consider only active pods. Since this changes long-standing behavior, the `KubeletPodResourcesListUseActivePods` feature gate (enabled by default) can be disabled to restore the previous behavior. Users encountering regressions are encouraged to file an issue if they rely on the old behavior. ([#132028](https://github.com/kubernetes/kubernetes/pull/132028), [@ffromani](https://github.com/ffromani)) [SIG Node and Testing] -- Pods were not allowed to mix the usage of `user-namespaces` (`hostUsers: false`) and `volumeDevices`. Kubernetes returned an error in this case. ([#132868](https://github.com/kubernetes/kubernetes/pull/132868), [@rata](https://github.com/rata)) -- Reduced the 5s delay before tainting `node.kubernetes.io/unreachable:NoExecute` when a Node became unreachable. ([#120816](https://github.com/kubernetes/kubernetes/pull/120816), [@tnqn](https://github.com/tnqn)) [SIG Apps and Node] -- Removed defunct `make vet` target, please use `make lint` instead ([#132509](https://github.com/kubernetes/kubernetes/pull/132509), [@yongruilin](https://github.com/yongruilin)) [SIG Testing] -- Removed the deprecated flag `--wait-interval` for the `ip6tables-legacy-restore` binary. ([#132352](https://github.com/kubernetes/kubernetes/pull/132352), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- ReplicaSets and Deployments should always count `.status.availableReplicas` at the correct time without a delay. This results in faster reconciliation of Deployment conditions and faster, unblocked Deployment rollouts. ([#132121](https://github.com/kubernetes/kubernetes/pull/132121), [@atiratree](https://github.com/atiratree)) [SIG Apps] -- Resolved a bug where DaemonSet updates unnecessarily triggered duplicate validation due to overlapping calls to `ValidateDaemonSet` and ValidateDaemonSetUpdate. This redundancy has been removed to prevent repeated validation runs. ([#132548](https://github.com/kubernetes/kubernetes/pull/132548), [@gavinkflam](https://github.com/gavinkflam)) -- Skipped pod backoff entirely when the `PodMaxBackoffDuration` kube-scheduler option was set to zero and the `SchedulerPopFromBackoffQ` feature gate was enabled. ([#131965](https://github.com/kubernetes/kubernetes/pull/131965), [@macsko](https://github.com/macsko)) -- Stopped expanding PVCs annotated with node-expand-not-required. ([#131907](https://github.com/kubernetes/kubernetes/pull/131907), [@gnufied](https://github.com/gnufied)) [SIG API Machinery, Etcd, Node, Storage and Testing] -- Stopped expanding the volume on the node if controller-side expansion was already completed. ([#131868](https://github.com/kubernetes/kubernetes/pull/131868), [@gnufied](https://github.com/gnufied)) -- Stopped logging error events when waiting for expansion on the kubelet. ([#131408](https://github.com/kubernetes/kubernetes/pull/131408), [@gnufied](https://github.com/gnufied)) -- Stopped removing the CSI JSON file if the volume was already mounted during subsequent errors. ([#131311](https://github.com/kubernetes/kubernetes/pull/131311), [@gnufied](https://github.com/gnufied)) -- The `baseline` and `restricted` pod security admission levels blocked setting the `host` field on probe and lifecycle handlers. ([#125271](https://github.com/kubernetes/kubernetes/pull/125271), [@tssurya](https://github.com/tssurya)) [SIG Auth, Node and Testing] -- The garbage collection controller no longer raced with changes to `ownerReferences` when deleting orphaned objects. ([#132632](https://github.com/kubernetes/kubernetes/pull/132632), [@sdowell](https://github.com/sdowell)) [SIG API Machinery and Apps] -- The shorthand for --output flag in kubectl explain was accidentally deleted, but has been added back. ([#131962](https://github.com/kubernetes/kubernetes/pull/131962), [@superbrothers](https://github.com/superbrothers)) [SIG CLI] -- Updated Windows `kube-proxy` to align with Linux behavior by correctly honoring the port specified in `EndpointSlice` for internal traffic routing. ([#132647](https://github.com/kubernetes/kubernetes/pull/132647), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] -- Updated `kube-proxy` with `nftables` to reject or drop traffic to services with no endpoints from filter chains at priority 0 (`NF_IP_PRI_FILTER`). ([#132456](https://github.com/kubernetes/kubernetes/pull/132456), [@aroradaman](https://github.com/aroradaman)) -- Updated `kubectl get job` to display the `SuccessCriteriaMet` status for listed jobs. ([#132832](https://github.com/kubernetes/kubernetes/pull/132832), [@Goend](https://github.com/Goend)) [SIG Apps and CLI] -- Updated the HPA controller so that it no longer emitted a `FailedRescale` event if a scale operation initially failed due to a conflict but succeeded after a retry; it now emitted a `SuccessfulRescale` event in this case. A `FailedRescale` event was still emitted if all retries were exhausted. ([#132007](https://github.com/kubernetes/kubernetes/pull/132007), [@AumPatel1](https://github.com/AumPatel1)) [SIG Apps and Autoscaling] -- `Statefulset` respected `minReadySeconds`. ([#130909](https://github.com/kubernetes/kubernetes/pull/130909), [@Edwinhr716](https://github.com/Edwinhr716)) -- `kubectl create|delete|get|replace --raw` commands now honored the server root paths specified in the kubeconfig file. ([#131165](https://github.com/kubernetes/kubernetes/pull/131165), [@liggitt](https://github.com/liggitt)) - -### Other (Cleanup or Flake) - -- Added a warning to `kubectl attach`, notifying / reminding users that commands and output are available via the `log` subresource of that Pod. ([#127183](https://github.com/kubernetes/kubernetes/pull/127183), [@mochizuki875](https://github.com/mochizuki875)) [SIG Auth, CLI, Node and Security] -- Added support for encoding and decoding types that implement the standard library interfaces `json.Marshaler`, `json.Unmarshaler`, `encoding.TextMarshaler`, or `encoding.TextUnmarshaler` to and from CBOR by transcoding. ([#132935](https://github.com/kubernetes/kubernetes/pull/132935), [@benluddy](https://github.com/benluddy)) -- Bumped kube-dns to v1.26.4. ([#132012](https://github.com/kubernetes/kubernetes/pull/132012), [@pacoxu](https://github.com/pacoxu)) -- Bumped the cel-go dependency to v0.25.0. The changeset is available at: https://github.com/google/cel-go/compare/v0.23.2...v0.25.0. ([#131444](https://github.com/kubernetes/kubernetes/pull/131444), [@erdii](https://github.com/erdii)) [SIG API Machinery, Auth, Cloud Provider and Node] -- By default, binaries like kube-apiserver were built with the `grpcnotrace` tag enabled. Used the `DBG` flag to enable Golang tracing if needed. ([#132210](https://github.com/kubernetes/kubernetes/pull/132210), [@dims](https://github.com/dims)) -- Changed Job controller to use the controller UID index for Pod lookups to improve performance. ([#132305](https://github.com/kubernetes/kubernetes/pull/132305), [@xigang](https://github.com/xigang)) -- Changed apiserver to treat failures decoding a mutating webhook patch as failures to call the webhook so they trigger the webhook failurePolicy and count against metrics like `webhook_fail_open_count` ([#131627](https://github.com/kubernetes/kubernetes/pull/131627), [@dims](https://github.com/dims)) [SIG API Machinery] -- Crane digest gcr.io/k8s-staging-e2e-test-images/agnhost:2.56 ([#132117](https://github.com/kubernetes/kubernetes/pull/132117), [@yashsingh74](https://github.com/yashsingh74)) [SIG Network and Testing] -- DRA kubelet- Updated logging to use `driverName` instead of `pluginName`, aligning with the rest of the Kubernetes components. ([#132096](https://github.com/kubernetes/kubernetes/pull/132096), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- DRA kubelet: Simplified recovery from mistakes like scheduling a Pod onto a node where the required driver was not running, as the kubelet no longer unnecessarily blocked Pod deletion. ([#131968](https://github.com/kubernetes/kubernetes/pull/131968), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- Fixed some missing white spaces in the flag descriptions and logs. ([#131562](https://github.com/kubernetes/kubernetes/pull/131562), [@logica0419](https://github.com/logica0419)) [SIG Network] -- Hack/update-codegen.sh now automatically ensured goimports and protoc. ([#131459](https://github.com/kubernetes/kubernetes/pull/131459), [@BenTheElder](https://github.com/BenTheElder)) -- Increased test coverage for kubelet package to 92.3%. ([#132484](https://github.com/kubernetes/kubernetes/pull/132484), [@ylink-lfs](https://github.com/ylink-lfs)) -- Kube-apiserver: removed the deprecated `apiserver_encryption_config_controller_automatic_reload_success_total` and `apiserver_encryption_config_controller_automatic_reload_failure_total` metrics in favor of `apiserver_encryption_config_controller_automatic_reloads_total`. ([#132238](https://github.com/kubernetes/kubernetes/pull/132238), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-scheduler: removed the deprecated scheduler_scheduler_cache_size metric in favor of scheduler_cache_size ([#131425](https://github.com/kubernetes/kubernetes/pull/131425), [@carlory](https://github.com/carlory)) [SIG Scheduling] -- Kubeadm: fixed missing space when printing the warning about pause image mismatch. ([#131563](https://github.com/kubernetes/kubernetes/pull/131563), [@logica0419](https://github.com/logica0419)) [SIG Cluster Lifecycle] -- Kubeadm: made the coredns deployment manifest use named ports consistently for the liveness and readiness probes. ([#131587](https://github.com/kubernetes/kubernetes/pull/131587), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] -- Kubectl interactive delete: treat empty newline input as N ([#132251](https://github.com/kubernetes/kubernetes/pull/132251), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] -- Masked access to Linux thermal interrupt information exposed via `/proc` and `/sys`. ([#131018](https://github.com/kubernetes/kubernetes/pull/131018), [@saschagrunert](https://github.com/saschagrunert)) -- Migrated Memory Manager to contextual logging. ([#130727](https://github.com/kubernetes/kubernetes/pull/130727), [@swatisehgal](https://github.com/swatisehgal)) -- Migrated `pkg/kubelet/status` to use contextual logging. ([#130852](https://github.com/kubernetes/kubernetes/pull/130852), [@Chulong-Li](https://github.com/Chulong-Li)) -- Migrated `pkg/kubelet/volumemanager` to contextual logging. ([#131306](https://github.com/kubernetes/kubernetes/pull/131306), [@Chulong-Li](https://github.com/Chulong-Li)) -- Migrated `pkg/kubelet/winstats` to contextual logging. ([#131001](https://github.com/kubernetes/kubernetes/pull/131001), [@Chulong-Li](https://github.com/Chulong-Li)) -- NONW ([#132890](https://github.com/kubernetes/kubernetes/pull/132890), [@atiratree](https://github.com/atiratree)) [SIG Apps] -- Promoted the `SeparateTaintEvictionController` feature gate to GA; it is now enabled unconditionally. ([#122634](https://github.com/kubernetes/kubernetes/pull/122634), [@carlory](https://github.com/carlory)) [SIG API Machinery, Apps, Node and Testing] -- Promoted the `apiserver_authentication_config_controller_automatic_reloads_total` and `apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds` metrics to BETA. ([#131798](https://github.com/kubernetes/kubernetes/pull/131798), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Instrumentation] -- Promoted the `apiserver_authorization_config_controller_automatic_reloads_total` and `apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds` metrics to BETA. ([#131768](https://github.com/kubernetes/kubernetes/pull/131768), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Instrumentation] -- Promoted two `EndpointSlice` tests to conformance to ensure that service proxy implementations rely on `EndpointSlices` instead of `Endpoints`. ([#132019](https://github.com/kubernetes/kubernetes/pull/132019), [@danwinship](https://github.com/danwinship)) [SIG Architecture, Network and Testing] -- Reduced excessive logging in the Volume Binding scheduler plugin by lowering the verbosity of high-frequency messages from `V(4)` to `V(5)`. ([#132840](https://github.com/kubernetes/kubernetes/pull/132840), [@ppmechlinski](https://github.com/ppmechlinski)) [SIG Autoscaling, Scheduling and Storage] -- Removed deprecated gogo protocol definitions from `k8s.io/externaljwt` in favor of `google.golang.org/protobuf`. ([#132772](https://github.com/kubernetes/kubernetes/pull/132772), [@saschagrunert](https://github.com/saschagrunert)) [SIG Auth] -- Removed deprecated gogo protocol definitions from `k8s.io/kms/apis` in favor of `google.golang.org/protobuf`. ([#132833](https://github.com/kubernetes/kubernetes/pull/132833), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery, Auth and Testing] -- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/deviceplugin` in favor of `google.golang.org/protobuf`. ([#133028](https://github.com/kubernetes/kubernetes/pull/133028), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node and Testing] -- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/podresources` in favor of `google.golang.org/protobuf`. ([#133027](https://github.com/kubernetes/kubernetes/pull/133027), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node and Testing] -- Removed general available feature-gate `DevicePluginCDIDevices`. ([#132083](https://github.com/kubernetes/kubernetes/pull/132083), [@carlory](https://github.com/carlory)) [SIG Node and Testing] -- Removed generally available feature-gate `PodDisruptionConditions`. ([#129501](https://github.com/kubernetes/kubernetes/pull/129501), [@carlory](https://github.com/carlory)) [SIG Apps] -- Removed support for API streaming from the REST client. ([#132285](https://github.com/kubernetes/kubernetes/pull/132285), [@p0lyn0mial](https://github.com/p0lyn0mial)) -- Removed support for API streaming from the `List()` method of the typed client. ([#132257](https://github.com/kubernetes/kubernetes/pull/132257), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- Removed support for API streaming from the dynamic client’s `List() method`. ([#132229](https://github.com/kubernetes/kubernetes/pull/132229), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery, CLI and Testing] -- Removed support for API streaming from the metadata client’s `List() method`. ([#132149](https://github.com/kubernetes/kubernetes/pull/132149), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- Removed the `kubernetes.io/initial-events-list-blueprint` annotation from the synthetic "Bookmark" event in watch stream requests. ([#132326](https://github.com/kubernetes/kubernetes/pull/132326), [@p0lyn0mial](https://github.com/p0lyn0mial)) -- Removed the deprecated `--register-schedulable` command line argument from the kubelet. ([#122384](https://github.com/kubernetes/kubernetes/pull/122384), [@carlory](https://github.com/carlory)) [SIG Cloud Provider, Node and Scalability] -- Replaced `toPtr` helper functions with the "k8s.io/utils/ptr" implementations. ([#132806](https://github.com/kubernetes/kubernetes/pull/132806), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps, Testing and Windows] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for ./test/e2e and ./test/utils. ([#132763](https://github.com/kubernetes/kubernetes/pull/132763), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Autoscaling and Testing] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for ./test/e2e. ([#132764](https://github.com/kubernetes/kubernetes/pull/132764), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth, Network, Node, Storage and Testing] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for ./test/e2e. ([#132765](https://github.com/kubernetes/kubernetes/pull/132765), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps, CLI and Testing] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for ./test/integration. ([#132762](https://github.com/kubernetes/kubernetes/pull/132762), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for apiextensions apiservers validation tests. ([#132726](https://github.com/kubernetes/kubernetes/pull/132726), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for apiextensions-apiserver pkg/controller. ([#132724](https://github.com/kubernetes/kubernetes/pull/132724), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for apiextensions-apiserver pkg/registry. ([#132725](https://github.com/kubernetes/kubernetes/pull/132725), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for pkg/apis (1/2). ([#132778](https://github.com/kubernetes/kubernetes/pull/132778), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps and Network] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for pkg/apis (2/2). ([#132779](https://github.com/kubernetes/kubernetes/pull/132779), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps, Auth and Storage] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for pkg/controller (1/2). ([#132781](https://github.com/kubernetes/kubernetes/pull/132781), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps and Network] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for pkg/controller (2/2). ([#132784](https://github.com/kubernetes/kubernetes/pull/132784), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps, Network, Node and Storage] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for pod-security-admission tests. ([#132741](https://github.com/kubernetes/kubernetes/pull/132741), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the apiextensions-apiservers integration tests. ([#132721](https://github.com/kubernetes/kubernetes/pull/132721), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the apiserver (2/2). ([#132752](https://github.com/kubernetes/kubernetes/pull/132752), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the cli-runtime. ([#132750](https://github.com/kubernetes/kubernetes/pull/132750), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG CLI and Release] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the cloud-provider. ([#132720](https://github.com/kubernetes/kubernetes/pull/132720), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Cloud Provider and Network] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the components-helper of the apimachinery. ([#132413](https://github.com/kubernetes/kubernetes/pull/132413), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the controller-manager. ([#132753](https://github.com/kubernetes/kubernetes/pull/132753), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Cloud Provider] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the csr. ([#132699](https://github.com/kubernetes/kubernetes/pull/132699), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the e2e_node. ([#132755](https://github.com/kubernetes/kubernetes/pull/132755), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Node and Testing] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the kubeapiserver. ([#132529](https://github.com/kubernetes/kubernetes/pull/132529), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Architecture] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the pkg/security and plugin/pkg. ([#132777](https://github.com/kubernetes/kubernetes/pull/132777), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth, Node and Release] -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the pod-security-admission admissiontests. ([#132742](https://github.com/kubernetes/kubernetes/pull/132742), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the pod-security-admission policy. ([#132743](https://github.com/kubernetes/kubernetes/pull/132743), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced deprecated package `k8s.io/utils/pointer` with `k8s.io/utils/ptr` for the reflector. ([#132698](https://github.com/kubernetes/kubernetes/pull/132698), [@PatrickLaabs](https://github.com/PatrickLaabs)) -- Replaced timer ptr helper function with the `k8s.io/utils/ptr` implementations. ([#133030](https://github.com/kubernetes/kubernetes/pull/133030), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- The deprecated `LegacySidecarContainers` feature gate was completely removed. ([#131463](https://github.com/kubernetes/kubernetes/pull/131463), [@gjkim42](https://github.com/gjkim42)) [SIG Node and Testing] -- Types: Code and Status moved from pkg/scheduler/framework to staging repo. - Users should update import path for these types from `k8s.io/kubernetes/pkg/scheduler/framework` to `k8s.io/kube-scheduler/framework`. ([#132087](https://github.com/kubernetes/kubernetes/pull/132087), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Types: CycleState, StateData, StateKey and ErrNotFound moved from pkg/scheduler/framework to k8s.io/kube-scheduler/framework. - Type CycleState that is passed to each plugin in scheduler framework is changed to the new interface CycleState (in k8s.io/kube-scheduler/framework) ([#131887](https://github.com/kubernetes/kubernetes/pull/131887), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Types: `ClusterEvent`, `ActionType`, `EventResource`, `ClusterEventWithHint`, `QueueingHint` and `QueueingHintFn` moved from `pkg/scheduler/framework` to `k8s.io/kube-scheduler/framework`. ([#132190](https://github.com/kubernetes/kubernetes/pull/132190), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Types: `NodeInfo`, `PodInfo`, `QueuedPodInfo`, `PodResource`, `AffinityTerm`, `WeightedAffinityTerm`, `Resource`, `ImageStateSummary`, `ProtocolPort` and `HostPortInfo` were moved from `pkg/scheduler/framework` to staging repo. - Users should update import path for these types from `k8s.io/kubernetes/pkg/scheduler/framework` to `k8s.io/kube-scheduler/framework` and update use of fields (to use getter/setter functions instead) where needed. ([#132457](https://github.com/kubernetes/kubernetes/pull/132457), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Updated CNI plugins to v1.7.1 ([#131602](https://github.com/kubernetes/kubernetes/pull/131602), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Cloud Provider, Node and Testing] -- Updated `conntrack` reconciler to consider a Service’s target port during cleanup of stale flow entries. ([#130542](https://github.com/kubernetes/kubernetes/pull/130542), [@aroradaman](https://github.com/aroradaman)) -- Updated `kubeadm` to use the `InitialCorruptCheck=true` etcd feature gate instead of the deprecated `--experimental-initial-corrupt-check` flag. Also replaced the use of `--experimental-watch-progress-notify-interval` with its graduated counterpart `--watch-progress-notify-interval`. ([#132838](https://github.com/kubernetes/kubernetes/pull/132838), [@AwesomePatrol](https://github.com/AwesomePatrol)) -- Updated cri-tools to v1.33.0. ([#131406](https://github.com/kubernetes/kubernetes/pull/131406), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider] -- Updated etcd version to v3.6.1. ([#132284](https://github.com/kubernetes/kubernetes/pull/132284), [@ArkaSaha30](https://github.com/ArkaSaha30)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] -- Updated the etcd client library to v3.6.4. ([#133226](https://github.com/kubernetes/kubernetes/pull/133226), [@ivanvc](https://github.com/ivanvc)) [SIG API Machinery, Auth, Cloud Provider and Node] -- Upgraded CoreDNS to v1.12.1. ([#131151](https://github.com/kubernetes/kubernetes/pull/131151), [@yashsingh74](https://github.com/yashsingh74)) [SIG Cloud Provider and Cluster Lifecycle] -- Upgraded functionality of `kubectl kustomize` as described at https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv5.7.0. ([#132593](https://github.com/kubernetes/kubernetes/pull/132593), [@koba1t](https://github.com/koba1t)) -- Validated APIVersion fields of the `HorizontalPodAutoscaler` to ensure that API objects were created and functioned properly. ([#132537](https://github.com/kubernetes/kubernetes/pull/132537), [@lalitc375](https://github.com/lalitc375)) [SIG Etcd and Testing] - -## Dependencies - -### Added -- buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go: 63bb56e -- github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp: [v1.26.0](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/tree/detectors/gcp/v1.26.0) -- github.com/bufbuild/protovalidate-go: [v0.9.1](https://github.com/bufbuild/protovalidate-go/tree/v0.9.1) -- github.com/envoyproxy/go-control-plane/envoy: [v1.32.4](https://github.com/envoyproxy/go-control-plane/tree/envoy/v1.32.4) -- github.com/envoyproxy/go-control-plane/ratelimit: [v0.1.0](https://github.com/envoyproxy/go-control-plane/tree/ratelimit/v0.1.0) -- github.com/go-jose/go-jose/v4: [v4.0.4](https://github.com/go-jose/go-jose/tree/v4.0.4) -- github.com/golang-jwt/jwt/v5: [v5.2.2](https://github.com/golang-jwt/jwt/tree/v5.2.2) -- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus: [v1.0.1](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/providers/prometheus/v1.0.1) -- github.com/grpc-ecosystem/go-grpc-middleware/v2: [v2.3.0](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/v2.3.0) -- github.com/spiffe/go-spiffe/v2: [v2.5.0](https://github.com/spiffe/go-spiffe/tree/v2.5.0) -- github.com/zeebo/errs: [v1.4.0](https://github.com/zeebo/errs/tree/v1.4.0) -- go.etcd.io/raft/v3: v3.6.0 -- go.opentelemetry.io/contrib/detectors/gcp: v1.34.0 -- go.opentelemetry.io/otel/sdk/metric: v1.34.0 -- go.yaml.in/yaml/v2: v2.4.2 -- go.yaml.in/yaml/v3: v3.0.4 -- sigs.k8s.io/structured-merge-diff/v6: v6.3.0 - -### Changed -- cel.dev/expr: v0.19.1 → v0.24.0 -- cloud.google.com/go/compute/metadata: v0.5.0 → v0.6.0 -- github.com/Microsoft/hnslib: [v0.0.8 → v0.1.1](https://github.com/Microsoft/hnslib/compare/v0.0.8...v0.1.1) -- github.com/cncf/xds/go: [b4127c9 → 2f00578](https://github.com/cncf/xds/compare/b4127c9...2f00578) -- github.com/coredns/corefile-migration: [v1.0.25 → v1.0.26](https://github.com/coredns/corefile-migration/compare/v1.0.25...v1.0.26) -- github.com/cpuguy83/go-md2man/v2: [v2.0.4 → v2.0.6](https://github.com/cpuguy83/go-md2man/compare/v2.0.4...v2.0.6) -- github.com/emicklei/go-restful/v3: [v3.11.0 → v3.12.2](https://github.com/emicklei/go-restful/compare/v3.11.0...v3.12.2) -- github.com/envoyproxy/go-control-plane: [v0.13.0 → v0.13.4](https://github.com/envoyproxy/go-control-plane/compare/v0.13.0...v0.13.4) -- github.com/envoyproxy/protoc-gen-validate: [v1.1.0 → v1.2.1](https://github.com/envoyproxy/protoc-gen-validate/compare/v1.1.0...v1.2.1) -- github.com/fsnotify/fsnotify: [v1.7.0 → v1.9.0](https://github.com/fsnotify/fsnotify/compare/v1.7.0...v1.9.0) -- github.com/fxamacker/cbor/v2: [v2.7.0 → v2.9.0](https://github.com/fxamacker/cbor/compare/v2.7.0...v2.9.0) -- github.com/golang/glog: [v1.2.2 → v1.2.4](https://github.com/golang/glog/compare/v1.2.2...v1.2.4) -- github.com/google/cel-go: [v0.23.2 → v0.26.0](https://github.com/google/cel-go/compare/v0.23.2...v0.26.0) -- github.com/google/gnostic-models: [v0.6.9 → v0.7.0](https://github.com/google/gnostic-models/compare/v0.6.9...v0.7.0) -- github.com/grpc-ecosystem/grpc-gateway/v2: [v2.24.0 → v2.26.3](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.24.0...v2.26.3) -- github.com/ishidawataru/sctp: [7ff4192 → ae8eb7f](https://github.com/ishidawataru/sctp/compare/7ff4192...ae8eb7f) -- github.com/jonboulle/clockwork: [v0.4.0 → v0.5.0](https://github.com/jonboulle/clockwork/compare/v0.4.0...v0.5.0) -- github.com/modern-go/reflect2: [v1.0.2 → 35a7c28](https://github.com/modern-go/reflect2/compare/v1.0.2...35a7c28) -- github.com/spf13/cobra: [v1.8.1 → v1.9.1](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1) -- github.com/spf13/pflag: [v1.0.5 → v1.0.6](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6) -- github.com/vishvananda/netlink: [62fb240 → v1.3.1](https://github.com/vishvananda/netlink/compare/62fb240...v1.3.1) -- github.com/vishvananda/netns: [v0.0.4 → v0.0.5](https://github.com/vishvananda/netns/compare/v0.0.4...v0.0.5) -- go.etcd.io/bbolt: v1.3.11 → v1.4.2 -- go.etcd.io/etcd/api/v3: v3.5.21 → v3.6.4 -- go.etcd.io/etcd/client/pkg/v3: v3.5.21 → v3.6.4 -- go.etcd.io/etcd/client/v3: v3.5.21 → v3.6.4 -- go.etcd.io/etcd/pkg/v3: v3.5.21 → v3.6.4 -- go.etcd.io/etcd/server/v3: v3.5.21 → v3.6.4 -- go.etcd.io/gofail: v0.1.0 → v0.2.0 -- go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful: v0.42.0 → v0.44.0 -- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc: v0.58.0 → v0.60.0 -- go.opentelemetry.io/contrib/propagators/b3: v1.17.0 → v1.19.0 -- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/exporters/otlp/otlptrace: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/metric: v1.33.0 → v1.35.0 -- go.opentelemetry.io/otel/sdk: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/trace: v1.33.0 → v1.35.0 -- go.opentelemetry.io/otel: v1.33.0 → v1.35.0 -- go.opentelemetry.io/proto/otlp: v1.4.0 → v1.5.0 -- google.golang.org/genproto/googleapis/api: e6fa225 → a0af3ef -- google.golang.org/genproto/googleapis/rpc: e6fa225 → a0af3ef -- google.golang.org/grpc: v1.68.1 → v1.72.1 -- k8s.io/gengo/v2: 1244d31 → 85fd79d -- k8s.io/kube-openapi: c8a335a → f3f2b99 -- k8s.io/system-validators: v1.9.1 → v1.10.1 -- k8s.io/utils: 3ea5e8c → 4c0f3b2 -- sigs.k8s.io/json: 9aa6b5e → cfa47c3 -- sigs.k8s.io/kustomize/api: v0.19.0 → v0.20.1 -- sigs.k8s.io/kustomize/cmd/config: v0.19.0 → v0.20.1 -- sigs.k8s.io/kustomize/kustomize/v5: v5.6.0 → v5.7.1 -- sigs.k8s.io/kustomize/kyaml: v0.19.0 → v0.20.1 -- sigs.k8s.io/yaml: v1.4.0 → v1.6.0 - -### Removed -- cloud.google.com/go/accessapproval: v1.7.4 -- cloud.google.com/go/accesscontextmanager: v1.8.4 -- cloud.google.com/go/aiplatform: v1.58.0 -- cloud.google.com/go/analytics: v0.22.0 -- cloud.google.com/go/apigateway: v1.6.4 -- cloud.google.com/go/apigeeconnect: v1.6.4 -- cloud.google.com/go/apigeeregistry: v0.8.2 -- cloud.google.com/go/appengine: v1.8.4 -- cloud.google.com/go/area120: v0.8.4 -- cloud.google.com/go/artifactregistry: v1.14.6 -- cloud.google.com/go/asset: v1.17.0 -- cloud.google.com/go/assuredworkloads: v1.11.4 -- cloud.google.com/go/automl: v1.13.4 -- cloud.google.com/go/baremetalsolution: v1.2.3 -- cloud.google.com/go/batch: v1.7.0 -- cloud.google.com/go/beyondcorp: v1.0.3 -- cloud.google.com/go/bigquery: v1.58.0 -- cloud.google.com/go/billing: v1.18.0 -- cloud.google.com/go/binaryauthorization: v1.8.0 -- cloud.google.com/go/certificatemanager: v1.7.4 -- cloud.google.com/go/channel: v1.17.4 -- cloud.google.com/go/cloudbuild: v1.15.0 -- cloud.google.com/go/clouddms: v1.7.3 -- cloud.google.com/go/cloudtasks: v1.12.4 -- cloud.google.com/go/compute: v1.23.3 -- cloud.google.com/go/contactcenterinsights: v1.12.1 -- cloud.google.com/go/container: v1.29.0 -- cloud.google.com/go/containeranalysis: v0.11.3 -- cloud.google.com/go/datacatalog: v1.19.2 -- cloud.google.com/go/dataflow: v0.9.4 -- cloud.google.com/go/dataform: v0.9.1 -- cloud.google.com/go/datafusion: v1.7.4 -- cloud.google.com/go/datalabeling: v0.8.4 -- cloud.google.com/go/dataplex: v1.14.0 -- cloud.google.com/go/dataproc/v2: v2.3.0 -- cloud.google.com/go/dataqna: v0.8.4 -- cloud.google.com/go/datastore: v1.15.0 -- cloud.google.com/go/datastream: v1.10.3 -- cloud.google.com/go/deploy: v1.17.0 -- cloud.google.com/go/dialogflow: v1.48.1 -- cloud.google.com/go/dlp: v1.11.1 -- cloud.google.com/go/documentai: v1.23.7 -- cloud.google.com/go/domains: v0.9.4 -- cloud.google.com/go/edgecontainer: v1.1.4 -- cloud.google.com/go/errorreporting: v0.3.0 -- cloud.google.com/go/essentialcontacts: v1.6.5 -- cloud.google.com/go/eventarc: v1.13.3 -- cloud.google.com/go/filestore: v1.8.0 -- cloud.google.com/go/firestore: v1.14.0 -- cloud.google.com/go/functions: v1.15.4 -- cloud.google.com/go/gkebackup: v1.3.4 -- cloud.google.com/go/gkeconnect: v0.8.4 -- cloud.google.com/go/gkehub: v0.14.4 -- cloud.google.com/go/gkemulticloud: v1.1.0 -- cloud.google.com/go/gsuiteaddons: v1.6.4 -- cloud.google.com/go/iam: v1.1.5 -- cloud.google.com/go/iap: v1.9.3 -- cloud.google.com/go/ids: v1.4.4 -- cloud.google.com/go/iot: v1.7.4 -- cloud.google.com/go/kms: v1.15.5 -- cloud.google.com/go/language: v1.12.2 -- cloud.google.com/go/lifesciences: v0.9.4 -- cloud.google.com/go/logging: v1.9.0 -- cloud.google.com/go/longrunning: v0.5.4 -- cloud.google.com/go/managedidentities: v1.6.4 -- cloud.google.com/go/maps: v1.6.3 -- cloud.google.com/go/mediatranslation: v0.8.4 -- cloud.google.com/go/memcache: v1.10.4 -- cloud.google.com/go/metastore: v1.13.3 -- cloud.google.com/go/monitoring: v1.17.0 -- cloud.google.com/go/networkconnectivity: v1.14.3 -- cloud.google.com/go/networkmanagement: v1.9.3 -- cloud.google.com/go/networksecurity: v0.9.4 -- cloud.google.com/go/notebooks: v1.11.2 -- cloud.google.com/go/optimization: v1.6.2 -- cloud.google.com/go/orchestration: v1.8.4 -- cloud.google.com/go/orgpolicy: v1.12.0 -- cloud.google.com/go/osconfig: v1.12.4 -- cloud.google.com/go/oslogin: v1.13.0 -- cloud.google.com/go/phishingprotection: v0.8.4 -- cloud.google.com/go/policytroubleshooter: v1.10.2 -- cloud.google.com/go/privatecatalog: v0.9.4 -- cloud.google.com/go/pubsub: v1.34.0 -- cloud.google.com/go/pubsublite: v1.8.1 -- cloud.google.com/go/recaptchaenterprise/v2: v2.9.0 -- cloud.google.com/go/recommendationengine: v0.8.4 -- cloud.google.com/go/recommender: v1.12.0 -- cloud.google.com/go/redis: v1.14.1 -- cloud.google.com/go/resourcemanager: v1.9.4 -- cloud.google.com/go/resourcesettings: v1.6.4 -- cloud.google.com/go/retail: v1.14.4 -- cloud.google.com/go/run: v1.3.3 -- cloud.google.com/go/scheduler: v1.10.5 -- cloud.google.com/go/secretmanager: v1.11.4 -- cloud.google.com/go/security: v1.15.4 -- cloud.google.com/go/securitycenter: v1.24.3 -- cloud.google.com/go/servicedirectory: v1.11.3 -- cloud.google.com/go/shell: v1.7.4 -- cloud.google.com/go/spanner: v1.55.0 -- cloud.google.com/go/speech: v1.21.0 -- cloud.google.com/go/storagetransfer: v1.10.3 -- cloud.google.com/go/talent: v1.6.5 -- cloud.google.com/go/texttospeech: v1.7.4 -- cloud.google.com/go/tpu: v1.6.4 -- cloud.google.com/go/trace: v1.10.4 -- cloud.google.com/go/translate: v1.10.0 -- cloud.google.com/go/video: v1.20.3 -- cloud.google.com/go/videointelligence: v1.11.4 -- cloud.google.com/go/vision/v2: v2.7.5 -- cloud.google.com/go/vmmigration: v1.7.4 -- cloud.google.com/go/vmwareengine: v1.0.3 -- cloud.google.com/go/vpcaccess: v1.7.4 -- cloud.google.com/go/webrisk: v1.9.4 -- cloud.google.com/go/websecurityscanner: v1.6.4 -- cloud.google.com/go/workflows: v1.12.3 -- cloud.google.com/go: v0.112.0 -- github.com/BurntSushi/toml: [v0.3.1](https://github.com/BurntSushi/toml/tree/v0.3.1) -- github.com/census-instrumentation/opencensus-proto: [v0.4.1](https://github.com/census-instrumentation/opencensus-proto/tree/v0.4.1) -- github.com/client9/misspell: [v0.3.4](https://github.com/client9/misspell/tree/v0.3.4) -- github.com/cncf/udpa/go: [269d4d4](https://github.com/cncf/udpa/tree/269d4d4) -- github.com/ghodss/yaml: [v1.0.0](https://github.com/ghodss/yaml/tree/v1.0.0) -- github.com/go-kit/kit: [v0.9.0](https://github.com/go-kit/kit/tree/v0.9.0) -- github.com/go-logfmt/logfmt: [v0.4.0](https://github.com/go-logfmt/logfmt/tree/v0.4.0) -- github.com/go-stack/stack: [v1.8.0](https://github.com/go-stack/stack/tree/v1.8.0) -- github.com/golang-jwt/jwt/v4: [v4.5.2](https://github.com/golang-jwt/jwt/tree/v4.5.2) -- github.com/golang/mock: [v1.1.1](https://github.com/golang/mock/tree/v1.1.1) -- github.com/google/shlex: [e7afc7f](https://github.com/google/shlex/tree/e7afc7f) -- github.com/grpc-ecosystem/grpc-gateway: [v1.16.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.16.0) -- github.com/konsorten/go-windows-terminal-sequences: [v1.0.1](https://github.com/konsorten/go-windows-terminal-sequences/tree/v1.0.1) -- github.com/kr/logfmt: [b84e30a](https://github.com/kr/logfmt/tree/b84e30a) -- github.com/opentracing/opentracing-go: [v1.1.0](https://github.com/opentracing/opentracing-go/tree/v1.1.0) -- go.etcd.io/etcd/client/v2: v2.305.21 -- go.etcd.io/etcd/raft/v3: v3.5.21 -- go.uber.org/atomic: v1.7.0 -- golang.org/x/lint: d0100b6 -- google.golang.org/appengine: v1.4.0 -- google.golang.org/genproto: ef43131 -- honnef.co/go/tools: ea95bdf -- sigs.k8s.io/structured-merge-diff/v4: v4.6.0 - - - -# v1.34.0-rc.2 - - -## Downloads for v1.34.0-rc.2 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes.tar.gz) | f7bdadc269da91b2892d45a493bc673bd7a2f309e52d0af00e08c28ec15fd52fecfb0f53e1eb32098a910ca4cb5f4824f50ffa855fd1ca1f8ad4426223633227 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-src.tar.gz) | 6272b6ac799dae382779592c890e838ce1c0264a4b300a501ba3952329d177f9b07b9e1ddb2636327d8e95b572ee2da9d08824c6d88d7c3a8342323360b6d1df - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-darwin-amd64.tar.gz) | 453293b8fe62dfea905bb7d6859e684ae46ebd1c32d454e06d424a3adf78f3c05d8e5d3b82fcfac7707ccf55ce05e1936b3808e2190ae33fedf6f58b8300d1e6 -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-darwin-arm64.tar.gz) | 4ac21ca50bf0ace0f876c43658e34715ea49671542db9fad15028b4e49ddd078ddb449f674bd76bfc39d5854bb12864e4811554148381b207582b510ed73ae11 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-386.tar.gz) | 300a21a090e564290f200203ef276f1829f1cb8a59ea4cfdfc78f5bf08a092d5241d19926f6958c8d5677aba57a2871f20e1764c15d3d223249e741c04666b58 -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-amd64.tar.gz) | d283a216e442eed1664f32cdb5a2cad47011bc5ee49cbfd072a9cfd9f9970e578aed3f90cfebe5a45fc6194fd720ebb035ebb4cb5a151f638899133d88c2a41b -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-arm.tar.gz) | 08e500b65bac726f984fde0f2d3af74ad6d3f2e6c2b58c34b0732503960b715c011f81cfdc9a6ef9f77adbc585f0f5b2b94993dd705a2cde20e9baec3cb11e9f -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-arm64.tar.gz) | 111702bec96f578ef6aaa9f42ba77146d5246f50e5d307275261fcbf9bdb7a569a9963400165659436dfdbed5640ad22cebd8603dcef4de671eed5072919b96c -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-ppc64le.tar.gz) | 98a48f18d9038f4553d1e0cd0e23c19f5e5dfaaabe7d35fcff1cd79340e6c38f0967d9a94c18c3e177790170acc7663bd98a3f6e6b9eeb05ce4d302d7d9bcbee -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-linux-s390x.tar.gz) | b2006580603819b01b9300eaa9bae34133ee3ca2ee86314ca890aba472a81b833bb0ef4e9eb07b46a8ae0074d5b0c9c826129d338720dae63752c0d92736002b -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-windows-386.tar.gz) | ee20616424acf66d0499f1228016324a634da3e53ed2728d33269fb06cafb96b7d2c8585a253ba9b05d43a51ee72a034a73000d3672ba1239b7cf75be9d10089 -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-windows-amd64.tar.gz) | 1f3a69ccac5cd00be47a4e89adfcd4bcbfe203895e98d8cc51282620a414a269891337cead1763ff7160b75b93fe48689d2cfd2467b8872f6e5fdc095fae0660 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-client-windows-arm64.tar.gz) | b619d292bc6427328a319c9ffc5e3e22039b89e8a875fa73c8cc2c6c767a066d6b1dd1984178a7035dd2eb38a468089f6259a6ba72506e32ab1249354a2590f0 - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-server-linux-amd64.tar.gz) | ec025926ab67e9307763266fbf178b533af679c3e371297dafd204981d1ecc9fd6cff34986a6580b2106d2ddab7f78ad66b1e01b223b612f0847c530922a41b2 -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-server-linux-arm64.tar.gz) | 0cf717718ed42283e8065d80afa18589dd2cb16f985811f25f8bf27302f4cbe5eb07bd264562a17798678047631584c10cd015c94982a91e7cd971cea8833b27 -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-server-linux-ppc64le.tar.gz) | d2e91d34fb64832ee3142289adbe61a025de832f5b594e93046b6ec4e3d5c557fd564236e095d7673a243dbfe59cd6d56475d97bc741dfb899c7db9036e8af5a -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-server-linux-s390x.tar.gz) | f36021108f5e5c9c8223dd1044579c6321fab4567ab6fdbb476ce81791ae5c09672f2121e9156f9166f8fd774a0d8ecddb4915093038ff617d2a0f3014f2a0cf - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-node-linux-amd64.tar.gz) | eda824ac2a18a02a655bdf23137c9196698a6688acb97f147f97ee430054a2d93da1ebdd5a75a5862435d311aeb3841e4e6592290831a8cb51da4f99a463777d -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-node-linux-arm64.tar.gz) | e0bd74348eb3c6d973b9a71c2114e9141eec6dc8448b38669261c7233254ece002e77971dc5de41ddbb1f34f9e2597f9429b0939123de889ee39e9dfee465633 -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-node-linux-ppc64le.tar.gz) | 737d536c7f3b458fc222e8ff600f3b4d71cb6f832cdea7900f0d0b4ceb473aa018cb6848b69b2c4609fe6a9d2681a666927678d98ce0fb959ad1a54c2bf046ac -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-node-linux-s390x.tar.gz) | 1a5dee2a9816128069fb3bde1bfec5f07e521425c1daa4b5538a18369850c186cff0a3e2a5f6f29325b83f275de86b26e4b5eea6b70297fb406a5d98eb77c8db -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.2/kubernetes-node-windows-amd64.tar.gz) | 51026e3f0ae4820fee2be15bef726deab5f8bc5e1ad33b5ca6f208e453a5bfd4ec161210d0ade1252ef899200b973f547e768d2d48a74b1af1514cdd9902d590 - -### 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.0-rc.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.0-rc.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.0-rc.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.0-rc.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.0-rc.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.0-rc.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.0-rc.1 - -## Changes by Kind - -### Feature - -- Kubernetes is now built using Go 1.24.6 ([#133516](https://github.com/kubernetes/kubernetes/pull/133516), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] - -### Documentation - -- Document how to contribute to staging repositories by redirecting to the main kubernetes repo ([#133570](https://github.com/kubernetes/kubernetes/pull/133570), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] - -### Bug or Regression - -- Changed the node restrictions to disallow the node to change it's ownerReferences. ([#133467](https://github.com/kubernetes/kubernetes/pull/133467), [@natherz97](https://github.com/natherz97)) [SIG Auth] -- DRA: fixed a data race which, depending on timing, could have led to broken allocation of devices backing extended resources ([#133587](https://github.com/kubernetes/kubernetes/pull/133587), [@pohly](https://github.com/pohly)) [SIG Node] -- Fixes a regression in 1.34 release candidates where object storage count metrics could include all instances of all types instead of only the object count for a particular type ([#133604](https://github.com/kubernetes/kubernetes/pull/133604), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] - -## Dependencies - -### Added -_Nothing has changed._ - -### Changed -_Nothing has changed._ - -### Removed -_Nothing has changed._ - - - -# v1.34.0-rc.1 - - -## Downloads for v1.34.0-rc.1 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes.tar.gz) | 0685ab08c9f1c9696b66d17c6bb480c5e880b217b4cdd239f5c3884f2bdbf0052544724d9e366e5ce51d86008176bfccf6406b088b0d97336f8e1d436894aea8 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-src.tar.gz) | 845873a513e35e3b54c68b578825a47252591558b53781948de660494355e8d3b658a337f2bb096634b47a7f72a6bec2ed02285b1fd08d772d562011f310a8c4 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-darwin-amd64.tar.gz) | b24ab87ffd978869c4b2d20190695225a6cdc827c2db5ca9774dfbdc7d879b57e7474b1fcd006fe8169ed37a020e194a99134aaebd90188c86ce85ffde71b2ab -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-darwin-arm64.tar.gz) | 0c8826535a3836d55405acce38c3a58e4fbd488c8933e9eced8e59deeba26ae583908062d809ff4b319c891a9ef7ac2bb736e3b7c665b7f9a47e65387f0ec964 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-386.tar.gz) | 4c6d920d2ab1003fcb5fc4b815337fd3b571256d64471fa0613a1d1f394f3ca55b9ac4f80bdb3921fd57ffc2887181f004abb252c23f0ed42e26a1bdbe26e9ca -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-amd64.tar.gz) | e1a0e3c2552fef6a21acef1ce1fb0f9a15c6b86c6b7b1c34fa8e6b60d2b341fe8fd3208555474cf4a1f2f4cffd90d175f866e508900ddf3d47178a24f6f6ae64 -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-arm.tar.gz) | bc4d687d8a8aa906a1e91e36dca875283aec2ec42c9a45db8c23dd38e7b9fb30e48c6bbf118c582717d3c0dc28cb1b198b90b167790ebbdaa9e33c7a29500187 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-arm64.tar.gz) | c1a460ec2698034181261d8678fb4ae4addbd21b80850539b7c38bed402f664dabaab76c0aff23a283516aeddc45658ab1b619d82d17590ca9f8e3d9a0268d8d -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-ppc64le.tar.gz) | 6ff03ab89e77ef519f5dd81ff547ca3267b71a6fe6b876409282b39e90f7af1531d20d50266487d7ab75a9154cb9260d546b8dcd5868180175c76c538baa93cb -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-linux-s390x.tar.gz) | 4b25feee780e0b6cfb7b9b9f263cea678ee2436ae4d674babaa5729abe065d07f1e8faa44b8881272716d703cffa7ee3712d6f9405f5ee825828ad9ca7d9d675 -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-windows-386.tar.gz) | 9a561196c73f4a2208347eb2bc8b98d462049798ea48961d3cc19ab9165d1985825f16014d6f4ba5ec19019e9c4dee7975415b7ef58d52d775b16838478b9708 -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-windows-amd64.tar.gz) | 9a8540a961c27395748c1d69988d4e93135b620e99a76cdbad403e42af41ef174eff50f590c0226d7431084134cf8b8aa7afb267024660ce8116117e7dde96d5 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-client-windows-arm64.tar.gz) | 5f7388b575dbf909423bc7b08e9a2b580d74c2827fd7740120d28f14b7d7cf1266c213dcda73268ad5acc030b1ef0f0a6c1d6683173661a37ddf34822306564a - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-server-linux-amd64.tar.gz) | cb719756e1e15e8b2dd656aae888d3b70478d12b9384636b8d6f2cd685f76dc8e79e94b8ee3f428e9e2f4503dc3a103b6918c9a5825c099723a14a314668fb5f -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-server-linux-arm64.tar.gz) | bf187239d1f2108e96d89503af0d8eed89608470470beb781dce0393263919d18c7ae3e6cb94c00d37413d295af135752c6d63de6abe11c717e96a4b2c1b549d -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-server-linux-ppc64le.tar.gz) | c140f4784438140ac65c672185c579e7b6fc5839bbcfa03390110726655b07e02b6c0a0adfc96446b4d4a8659c1831da74bfd5d83fc8a6d8cd2d6fcbf8750ed8 -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-server-linux-s390x.tar.gz) | cc1fe349b9b39b52bbe0e2172049a60ce97dc4e2d4d23007c07ccc8692d01c0ee22012c74e1712d19dd50d6a8c391a9894f7e0a02e3596389083f360c6744743 - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-node-linux-amd64.tar.gz) | daffae5fe6930b631fbfb68a9a3f9b7128c98a0a5916afde8de513b8967a4334c4cdb74a87ba0ab75e193e6fdfcfcf74136eaca059b3095be5e06c2828f2ab11 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-node-linux-arm64.tar.gz) | 0c4bc0530eea7b136b9a6977fa0212f43123f5215ab9e6c53739688890e2a88d7c4aee75cd09d1f7f03d645fa756b48bbea30b46b255527ba2bcc5f954fffab4 -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-node-linux-ppc64le.tar.gz) | 7c6da33a3f82e2e14274d7be6a329277ee4ee483e0d2e228d3500fc57201f6b6220e1fed3c4214c46ca2c660257c83cdd0157724f17cafd79b4ac47f98c2ce84 -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-node-linux-s390x.tar.gz) | 8ee20942b91808026b9e54c8964422cb7a6dcf4c081b26bf41ea910721dfdccb449eab7b7ea2e11ef0260693f7c48411e65c4b18ab8d0045d243020b674f93d4 -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.1/kubernetes-node-windows-amd64.tar.gz) | b9b112456539ef0dd4b08178cdf3c56e1723746fdbad2dddb3ca18075e1258591b4f9937cfc884fb64f5ef275b82c8f5a3d25e29307753bdb574f18bd56da358 - -### 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.0-rc.1](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.0-rc.1](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.0-rc.1](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.0-rc.1](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.0-rc.1](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.0-rc.1](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.0-rc.0 - -## Changes by Kind - -### Bug or Regression - -- Fixes an issue that caused kube-apiserver to panic in 1.34.0-rc.0 ([#133412](https://github.com/kubernetes/kubernetes/pull/133412), [@richabanker](https://github.com/richabanker)) [SIG API Machinery and Etcd] -- Make podcertificaterequestcleaner role feature-gated ([#133409](https://github.com/kubernetes/kubernetes/pull/133409), [@carlory](https://github.com/carlory)) [SIG Auth] - -## Dependencies - -### Added -_Nothing has changed._ - -### Changed -_Nothing has changed._ - -### Removed -_Nothing has changed._ - - - -# v1.34.0-rc.0 - - -## Downloads for v1.34.0-rc.0 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes.tar.gz) | 3a40163a162b703ca49714789d751c741cc09a4921e1dd1a3e51cd1e45285c43aa84c153a9130b105220defe573ecc9bc2f9e69e7ead50f470110eba0f3eb2e7 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-src.tar.gz) | f1e769b6bd1c24e88a445ba58c30448b4f138c36b4acb1de04616630eb8d74b01986c94d7ed2022943425e0e9ea8043253fe3f32a696c510187ccae2deb81334 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-darwin-amd64.tar.gz) | c5b135c912d5d942cdeff31b23f3d7865aa40252730e367f8170d70fbb2c695efbc04648787b8d36ec788ce761f71adde0c8bd92ab1bec714ded7e5b43f1e70e -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-darwin-arm64.tar.gz) | d2dc774dbf6ec52a6e848be7ede8d3640ec3969d6b79462ab5b06c654d65046961fecf7b0dee7861af7895beb65c90867f7f0b370d0437322f92d2afbe7ff999 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-386.tar.gz) | c5df62de030efad772ff72ce96776229a4cdf7c80c6e8625f46f32d7fe064b84021432eda05aa48d8dfd3b38f5d8bb1f0fec010fa39bdcc437d48f7dc02ce20c -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-amd64.tar.gz) | 789c6ffc56ed8772fdd7142865cd788d0975dbeb759533d00768c6ac964973d5553aaac8341c8d55604e70abdfdb3c5b1b91cf7f2a6fc08ccc4a5a0d93e76127 -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-arm.tar.gz) | f82bb1bc87d8288f54de7f5dbb54ff6e29dea210e0632348b3912cff326a78047da4dd05581dd29be2d9f7756eab1f2af1a9bf45818e378b22ccaf7c265803c1 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-arm64.tar.gz) | 977cd13d6ad03c3e3b820d69d9136acb90e0efc13253a6233cf2c0d819c0b68734672ab623a7496080417c750ee04d3ce276e41d541cd07deb31ff90b6c19ee7 -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-ppc64le.tar.gz) | 6bb7a7935989c301727e355b3db6774d2ca057f317c166ceddb3aee1d4d5c553b62dc28f851b8acf0dda73f64d41aec77e41721e9cd5c1d9e8ffb7afc318ff6c -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-linux-s390x.tar.gz) | e922a13265a591f989e6026321c792221663f09f062a5f7866fb4c1efa28a91216bc7f297b4c778eac8784f773d642ed873bcd6d97dfcff6897631a17c3d35d7 -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-windows-386.tar.gz) | 963016b3be20076dd94f46aac858d2e4fe3a973d54a574b0289c8e5e20ec192680b3e4cf9f813f17658f892c96adabfd467cb8a7ad6332007e4aea07a828572c -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-windows-amd64.tar.gz) | 01432a7dcdcd99c0c8e00bd9085dde13fb07a7091c19592bd7b7323932af56f47d2554b4806fc5ff867f7f04428a4572e0149bec32f7b9e1b3dc2dc0e63792d6 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-client-windows-arm64.tar.gz) | 38fefd2b9c6a5b37b461e1787ee824d8208bd76f375bb0f5740e4dc2ede98baa30532021629cfc9ccb04b854ce19c7ccde245e4edd3a7482336d70ea47dd09d4 - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-server-linux-amd64.tar.gz) | 4a63efa6876bc66683650361a071ddca8c201a653a3947e3939f68d32835b76357a5c63bebfdbd9e2420b485b9dfe42ea03ad6b230960207c21a98d96f24ddda -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-server-linux-arm64.tar.gz) | 0965a31c7db0dcbbe8160c420df3ca12923fa1bfc720103b93f3ba238c529667933477df0f190737595402173f38b1785179f468047d504e4a1bfbe969951df9 -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-server-linux-ppc64le.tar.gz) | b62515af8f866bcde5866b10def38c63bb0ef50d84dcac1b536dad85d04b10285e38bafd79e0d80fcab8820455597164e4202cc4848b1cc03137a967a1ca5597 -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-server-linux-s390x.tar.gz) | 17eaa46f21730ad8e809b827c053030249e8cfff812fbd0a2252650ef1b90ae4c98c57d54972317d806fcad7c77444f69c97436d1f652b7ac0a6ba908acee09a - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-node-linux-amd64.tar.gz) | 97bb8e23f59afae0f0a5d4a9b594dca25e76fc426d1e05d13582df44f207b913f82eb1b36e2669a267f0d8171978049e623556395a94f63ad30a873d41bb0a47 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-node-linux-arm64.tar.gz) | caa6625eec52dee92dec7f33936e7a5f3ba8166e1150b6c747048b74f1bb574dcc097fb7ce7a634f122d42a7fbdbf81e5b4a483a71adeed2c2937af8977114a8 -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-node-linux-ppc64le.tar.gz) | cab3bd432093fb03bdc6497f94dd501e412fb90eca9b350319239fcca5687012561f7342114b52e09bad61e6eea3109158ef92add94809d4b56f9aa3470e9e93 -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-node-linux-s390x.tar.gz) | 0c95a03a6539f99de244213359c6d4f42ebf6aea79c26c8cfb82d319b7c27dd5ca4050cc58ef27f999fba334fc67629a0b96bb7af66721309a203b1f0aec6467 -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-rc.0/kubernetes-node-windows-amd64.tar.gz) | 9f2e1f5e6227e41d0c681e07a2edc3eb4da4b6ba20125a4aad6b94c2023d29238257244b243e508c17c5693a8168da5bf66cf744a9610799d77adbed29363c45 - -### 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.0-rc.0](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.0-rc.0](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.0-rc.0](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.0-rc.0](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.0-rc.0](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.0-rc.0](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.0-beta.0 - -## Changes by Kind - -### Deprecation - -- DRA kubelet: gRPC API graduated to v1, v1beta1 is deprecated starting in 1.34. Updating DRA drivers to the k8s.io/dynamic-resource-allocation/kubeletplugin helper from 1.34 adds support for both API versions. ([#132700](https://github.com/kubernetes/kubernetes/pull/132700), [@pohly](https://github.com/pohly)) [SIG Node and Testing] - -### API Change - -- Add a new `FileKeyRef` field to containers, allowing them to load variables from files by setting this field. - - Introduce the EnvFiles feature gate to govern activation of this functionality. ([#132626](https://github.com/kubernetes/kubernetes/pull/132626), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG API Machinery, Apps, Node and Testing] -- Add driver-owned fields in ResourceSlice to mark whether the device is shareable among multiple resource claims (or requests) and to specify how each capacity can be shared between different requests. - - Add user-owned fields in ResourceClaim to specify resource requirements against each device capacity. - - Add scheduler-owned field in ResourceClaim.Status to specify how much device capacity is reserved for a specific request. - - Add an additional identifier to ResourceClaim.Status for the device supports multiple allocations. - - Add a new constraint type to enforce uniqueness of specified attributes across all allocated devices. ([#132522](https://github.com/kubernetes/kubernetes/pull/132522), [@sunya-ch](https://github.com/sunya-ch)) [SIG API Machinery, Apps, Architecture, CLI, Cluster Lifecycle, Network, Node, Release, Scheduling and Testing] -- Add new optional APIs in ResouceSlice.Basic and ResourceClaim.Status.AllocatedDeviceStatus. ([#130160](https://github.com/kubernetes/kubernetes/pull/130160), [@KobayashiD27](https://github.com/KobayashiD27)) [SIG API Machinery, Apps, Architecture, Node, Release, Scheduling and Testing] -- Added a mechanism for configurable container restarts: _container level restart rules_. This is an alpha feature behind the `ContainerRestartRules` feature gate. ([#132642](https://github.com/kubernetes/kubernetes/pull/132642), [@yuanwang04](https://github.com/yuanwang04)) [SIG API Machinery, Apps, Node and Testing] -- Added detailed event for in-place pod vertical scaling completed, improving cluster management and debugging ([#130387](https://github.com/kubernetes/kubernetes/pull/130387), [@shiya0705](https://github.com/shiya0705)) [SIG API Machinery, Apps, Autoscaling, Node, Scheduling and Testing] -- Added validation to reject Pods using the `PodLevelResources` feature on Windows OS due to lack of support. The API server rejects Pods with Pod-level resources and a `Pod.spec.os.name` targeting Windows. Kubelet on nodes running Windows also rejects Pods with Pod-level resources at admission phase. ([#133046](https://github.com/kubernetes/kubernetes/pull/133046), [@toVersus](https://github.com/toVersus)) [SIG Apps and Node] -- Adds warnings when creating headless service with set loadBalancerIP,externalIPs and/or SessionAffinity ([#132214](https://github.com/kubernetes/kubernetes/pull/132214), [@Peac36](https://github.com/Peac36)) [SIG Network] -- Allow pvc.spec.VolumeAttributesClassName to go from non-nil to nil ([#132106](https://github.com/kubernetes/kubernetes/pull/132106), [@AndrewSirenko](https://github.com/AndrewSirenko)) [SIG Apps] -- Allows setting the `hostnameOverride` field in `PodSpec` to specify any RFC 1123 DNS subdomain as the pod's hostname. The `HostnameOverride` feature gate has been introduced to control enablement of this functionality. ([#132558](https://github.com/kubernetes/kubernetes/pull/132558), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG API Machinery, Apps, Network, Node and Testing] -- AppArmor profiles specified in the pod or container SecurityContext are no longer copied to deprecated AppArmor annotations (prefix `container.apparmor.security.beta.kubernetes.io/`). Anything that inspects the deprecated annotations must be migrated to use the SecurityContext fields instead. ([#131989](https://github.com/kubernetes/kubernetes/pull/131989), [@tallclair](https://github.com/tallclair)) [SIG Node] -- Changes underlying logic to propagate Pod level hugepage cgroup to containers when they do not specify hugepage resources. - - Adds validation to enforce the hugepage aggregated container limits to be smaller or equal to pod-level limits. This was already enforced with the defaulted requests from the specified limits, however it did not make it clear about both hugepage requests and limits. ([#131089](https://github.com/kubernetes/kubernetes/pull/131089), [@KevinTMtz](https://github.com/KevinTMtz)) [SIG Apps, Node and Testing] -- DRA: the scheduler plugin now prevents abnormal filter runtimes by timing out after 10 seconds. This is configurable via the plugin configuration's `FilterTimeout`. Setting it to zero disables the timeout and restores the behavior of Kubernetes <= 1.33. ([#132033](https://github.com/kubernetes/kubernetes/pull/132033), [@pohly](https://github.com/pohly)) [SIG Node, Scheduling and Testing] -- DRA: when the prioritized list feature is used in a request and the resulting number of allocated devices exceeds the number of allowed devices per claim, the scheduler aborts the attempt to allocate devices early. Previously it tried to many different combinations, which can take a long time. ([#130593](https://github.com/kubernetes/kubernetes/pull/130593), [@mortent](https://github.com/mortent)) [SIG Apps, Node, Scheduling and Testing] -- Dynamic Resource Allocation: graduated core functionality to general availability (GA). This newly stable feature uses the _structured parameters_ flavor of DRA. ([#132706](https://github.com/kubernetes/kubernetes/pull/132706), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Autoscaling, Etcd, Node, Scheduling and Testing] -- Enable kube-apiserver support for PodCertificateRequest and PodCertificate projected volumes (behind the PodCertificateRequest feature gate). ([#128010](https://github.com/kubernetes/kubernetes/pull/128010), [@ahmedtd](https://github.com/ahmedtd)) [SIG API Machinery, Apps, Auth, Cloud Provider, Etcd, Node, Storage and Testing] -- Extended resources backed by DRA feature allows cluster operator to specify extendedResourceName in DeviceClass, and application operator to continue using extended resources in pod's requests to request for DRA devices matching the DeviceClass. - - NodeResourcesFit plugin scoring won't work for extended resources backed by DRA ([#130653](https://github.com/kubernetes/kubernetes/pull/130653), [@yliaog](https://github.com/yliaog)) [SIG API Machinery, Apps, Auth, Node, Scheduling and Testing] -- Fix prerelease lifecycle for PodCertificateRequest ([#133350](https://github.com/kubernetes/kubernetes/pull/133350), [@carlory](https://github.com/carlory)) [SIG Auth] -- Fixes a 1.33 regression that can cause a nil panic in kube-scheduler when aggregating resource requests across container's spec and status. ([#132895](https://github.com/kubernetes/kubernetes/pull/132895), [@yue9944882](https://github.com/yue9944882)) [SIG Node and Scheduling] -- Introduced the admissionregistration.k8s.io/v1beta1/MutatingAdmissionPolicy API type. - To enable, enable the `MutatingAdmissionPolicy` feature gate (which is off by default) and set `--runtime-config=admissionregistration.k8s.io/v1beta1=true` on the kube-apiserver. - Note that the default stored version remains alpha in 1.34 and whoever enabled beta during 1.34 needs to run a storage migration yourself to ensure you don't depend on alpha data in etcd. ([#132821](https://github.com/kubernetes/kubernetes/pull/132821), [@cici37](https://github.com/cici37)) [SIG API Machinery, Etcd and Testing] -- No, changes underlying logic for Eviction Manager helper functions ([#132277](https://github.com/kubernetes/kubernetes/pull/132277), [@KevinTMtz](https://github.com/KevinTMtz)) [SIG Node, Scheduling and Testing] -- Promote MutableCSINodeAllocatableCount to Beta. ([#132429](https://github.com/kubernetes/kubernetes/pull/132429), [@torredil](https://github.com/torredil)) [SIG Storage] -- Promoted feature-gate `VolumeAttributesClass` to GA - - Promoted API `VolumeAttributesClass` and `VolumeAttributesClassList` to `storage.k8s.io/v1`. ([#131549](https://github.com/kubernetes/kubernetes/pull/131549), [@carlory](https://github.com/carlory)) [SIG API Machinery, Apps, Auth, CLI, Etcd, Storage and Testing] -- Promoted the `APIServerTracing` feature gate to GA. The `--tracing-config-file` flag now accepts `TracingConfiguration` in version `apiserver.config.k8s.io/v1` (with no changes from `apiserver.config.k8s.io/v1beta1`). ([#132340](https://github.com/kubernetes/kubernetes/pull/132340), [@dashpole](https://github.com/dashpole)) [SIG API Machinery and Testing] -- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/pluginregistration` in favor of `google.golang.org/protobuf`. ([#132773](https://github.com/kubernetes/kubernetes/pull/132773), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node] -- The Kubelet can now monitor the health of devices allocated via Dynamic Resource Allocation (DRA) and report it in the `pod.status.containerStatuses.allocatedResourcesStatus` field. This requires the DRA plugin to implement the new v1alpha1 `NodeHealth` gRPC service. This feature is controlled by the `ResourceHealthStatus` feature gate. ([#130606](https://github.com/kubernetes/kubernetes/pull/130606), [@Jpsassine](https://github.com/Jpsassine)) [SIG Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Network, Node, Release, Scheduling, Storage and Testing] -- The KubeletServiceAccountTokenForCredentialProviders feature is now beta and enabled by default. ([#133017](https://github.com/kubernetes/kubernetes/pull/133017), [@aramase](https://github.com/aramase)) [SIG Auth and Node] -- The conditionType is "oneof" approved/denied check of CertificateSigningRequest's `.status.conditions` field has been migrated to declarative validation. - If the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate is enabled, declarative validation is the primary source of errors for migrated fields. ([#133013](https://github.com/kubernetes/kubernetes/pull/133013), [@aaron-prindle](https://github.com/aaron-prindle)) [SIG API Machinery and Auth] -- The fallback behavior of the Downward API's `resourceFieldRef` field has been updated to account for pod-level resources: if container-level limits are not set, pod-level limits are now used before falling back to node allocatable resources. ([#132605](https://github.com/kubernetes/kubernetes/pull/132605), [@toVersus](https://github.com/toVersus)) [SIG Node, Scheduling and Testing] -- The kubelet's image pull credential tracking now supports service account-based verification. When an image is pulled using service account credentials via external credential providers, subsequent pods using the same service account (UID, name, and namespace) can access the cached image without re-authentication for the lifetime of that service account. ([#132771](https://github.com/kubernetes/kubernetes/pull/132771), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] - -### Feature - -- API calls dispatched during pod scheduling are now executed asynchronously if the SchedulerAsyncAPICalls feature gate is enabled. - Out-of-tree plugins can use APIDispatcher and APICacher from the framework to dispatch their own calls. ([#132886](https://github.com/kubernetes/kubernetes/pull/132886), [@macsko](https://github.com/macsko)) [SIG Release, Scheduling and Testing] -- Add `started_user_namespaced_pods_total` and `started_user_namespaced_pods_errors_total` for tracking the successes and failures in creating pods if a user namespace is requested. ([#132902](https://github.com/kubernetes/kubernetes/pull/132902), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] -- Add apiserver_resource_size_estimate_bytes metric to apiserver ([#132893](https://github.com/kubernetes/kubernetes/pull/132893), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd and Instrumentation] -- Add memory tracking to scheduler performance tests to help detect memory leaks and monitor memory usage patterns while running scheduler_perf ([#132910](https://github.com/kubernetes/kubernetes/pull/132910), [@utam0k](https://github.com/utam0k)) [SIG Scheduling and Testing] -- Added 3 new metrics for monitoring async API calls in the scheduler when the SchedulerAsyncAPICalls feature gate is enabled: - - scheduler_async_api_call_execution_total: tracks executed API calls by call type and result (success/error) - - scheduler_async_api_call_duration_seconds: histogram of API call execution duration by call type and result - - scheduler_pending_async_api_calls: gauge showing current number of pending API calls in the queue ([#133120](https://github.com/kubernetes/kubernetes/pull/133120), [@utam0k](https://github.com/utam0k)) [SIG Release and Scheduling] -- Added machine readable output options (JSON & YAML) to kubectl api-resources ([#132604](https://github.com/kubernetes/kubernetes/pull/132604), [@dharmit](https://github.com/dharmit)) [SIG Apps, CLI and Network] -- Added support for a new kubectl output format, `kyaml`. KYAML is a strict subset of YAML and should be accepted by any YAML processor. The formatting of KYAML is halfway between JSON and YAML. Because it is more explicit than the default YAML style, it should be less error-prone. ([#132942](https://github.com/kubernetes/kubernetes/pull/132942), [@thockin](https://github.com/thockin)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Contributor Experience, Instrumentation, Network, Node, Scheduling, Storage and Testing] -- Adds HPA support to pod-level resource specifications. When the pod-level resource feature is enabled, HPAs configured with `Resource` type metrics will calculate the pod resources from `pod.Spec.Resources` field, if specified. ([#132430](https://github.com/kubernetes/kubernetes/pull/132430), [@laoj2](https://github.com/laoj2)) [SIG Apps, Autoscaling and Testing] -- Adds a `container_swap_limit_bytes` metric to expose the swap limit assigned to containers under the `LimitedSwap` swap behavior. ([#132348](https://github.com/kubernetes/kubernetes/pull/132348), [@iholder101](https://github.com/iholder101)) [SIG Node and Testing] -- Adds useful endpoints for kube-apiserver ([#132581](https://github.com/kubernetes/kubernetes/pull/132581), [@itssimrank](https://github.com/itssimrank)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] -- Bump KubeletCgroupDriverFromCRI to GA and add metric to track out of support CRI implementations ([#133157](https://github.com/kubernetes/kubernetes/pull/133157), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] -- CRI API has auth fields in image pulling marked as debug_redact. ([#133135](https://github.com/kubernetes/kubernetes/pull/133135), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) [SIG Node] -- Changed handling of CustomResourceDefinitions with unrecognized formats. Writing a schema with an unrecognized formats now triggers a warning (the write is still accepted). ([#133136](https://github.com/kubernetes/kubernetes/pull/133136), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery] -- DRAAdminAccess is enabled by default allowing users to create ResourceClaims and ResourceClaimTemplates in privileged mode to grant access to devices that are in use by other users for admin tasks like monitor health or status of the device. ([#133085](https://github.com/kubernetes/kubernetes/pull/133085), [@ritazh](https://github.com/ritazh)) [SIG Auth and Node] -- DRAPrioritizedList is now turned on by default which makes it possible to provide a prioritized list of subrequests in a ResourceClaim. ([#132767](https://github.com/kubernetes/kubernetes/pull/132767), [@mortent](https://github.com/mortent)) [SIG Node, Scheduling and Testing] -- Demote KEP-5278 feature gates ClearingNominatedNodeNameAfterBinding and NominatedNodeNameForExpectation to Alpha from Beta ([#133293](https://github.com/kubernetes/kubernetes/pull/133293), [@utam0k](https://github.com/utam0k)) [SIG Scheduling and Testing] -- Deprecate apiserver_storage_objects and replace it with apiserver_resource_objects metric using labels consistent with other metrics ([#132965](https://github.com/kubernetes/kubernetes/pull/132965), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd and Instrumentation] -- Ensure memory resizing for Guaranteed QOS pods on static Memory policy configured is gated by `InPlacePodVerticalScalingExclusiveMemory` (defaults to `false`). ([#132473](https://github.com/kubernetes/kubernetes/pull/132473), [@pravk03](https://github.com/pravk03)) [SIG Node, Scheduling and Testing] -- Fix recording the kubelet_container_resize_requests_total metric to include all resize-related updates. ([#133060](https://github.com/kubernetes/kubernetes/pull/133060), [@natasha41575](https://github.com/natasha41575)) [SIG Node] -- Graduate ListFromCacheSnapshot to Beta ([#132901](https://github.com/kubernetes/kubernetes/pull/132901), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Graduate `PodLevelResources` feature to beta and have it on by default. This feature allows defining CPU and memory resources for an entire pod in `pod.spec.resources`. ([#132999](https://github.com/kubernetes/kubernetes/pull/132999), [@ndixita](https://github.com/ndixita)) [SIG Node] -- Graduate `PodObservedGenerationTracking` feature to beta and have it on by default. This feature means that the top level `status.observedGeneration` and `status.conditions[].observedGeneration` fields in pods will now be populated to reflect the `metadata.generation` of the podspec at the time that the status or condition is being reported. ([#132912](https://github.com/kubernetes/kubernetes/pull/132912), [@natasha41575](https://github.com/natasha41575)) [SIG Apps, Node and Testing] -- Graduate the WinDSR feature in the kube-proxy to GA. The `WinDSR` feature gate is now enabled by default. ([#132108](https://github.com/kubernetes/kubernetes/pull/132108), [@rzlink](https://github.com/rzlink)) [SIG Network and Windows] -- Graduate the WinOverlay feature in the kube-proxy to GA. The **WinOverlay** feature gate is now enabled by default. ([#133042](https://github.com/kubernetes/kubernetes/pull/133042), [@rzlink](https://github.com/rzlink)) [SIG Network and Windows] -- Graduates the `WatchList` feature gate to Beta for kube-apiserver and enables `WatchListClient` for KCM. ([#132704](https://github.com/kubernetes/kubernetes/pull/132704), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- If PreBindPreFlight returns Skip, the scheduler doesn't run the plugin at PreBind. - If any PreBindPreFlight returns Success, the scheduler puts NominatedNodeName to the pod - so that other components (such as the cluster autoscaler) can notice the pod is going to be bound to the node. ([#133021](https://github.com/kubernetes/kubernetes/pull/133021), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Increase APF max seats to 100 for LIST requests ([#133034](https://github.com/kubernetes/kubernetes/pull/133034), [@serathius](https://github.com/serathius)) [SIG API Machinery] -- Introduce a method 'GetPCIeRootAttributeByPCIBusID(pciBusID)' for third-party DRA drivers to provide common logic for the standardized device attribute 'resource.kubernetes.io/pcieRoot' ([#132296](https://github.com/kubernetes/kubernetes/pull/132296), [@everpeace](https://github.com/everpeace)) [SIG Node] -- It will promote windows graceful shutdown feature from alpha to beta. ([#133062](https://github.com/kubernetes/kubernetes/pull/133062), [@zylxjtu](https://github.com/zylxjtu)) [SIG Windows] -- Kube-apiserver now reports the last configuration hash as a label in - - - `apiserver_authentication_config_controller_last_config_info` metric after successfully loading the authentication configuration file. - - `apiserver_authorization_config_controller_last_config_info` metric after successfully loading the authorization configuration file. - - `apiserver_encryption_config_controller_last_config_info` metric after successfully loading the encryption configuration file. ([#132299](https://github.com/kubernetes/kubernetes/pull/132299), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-apiserver: previously persisted CustomResourceDefinition objects with an invalid whitespace-only `caBundle` can now serve requests that do not require conversion. ([#132514](https://github.com/kubernetes/kubernetes/pull/132514), [@tiffanny29631](https://github.com/tiffanny29631)) [SIG API Machinery] -- Kube-controller-manager now reports the following metrics for ResourceClaims with admin access: - - `resourceclaim_controller_creates_total` count metric with labels admin_access (true or false), status (failure or success) to track the total number of ResourceClaims creation requests - - `resourceclaim_controller_resource_claims` gauge metric with labels admin_access (true or false), allocated (true or false) to track the current number of ResourceClaims ([#132800](https://github.com/kubernetes/kubernetes/pull/132800), [@ritazh](https://github.com/ritazh)) [SIG Apps, Auth, Instrumentation and Node] -- Kubelet now detects terminal CSI volume mount failures due to exceeded attachment limits on the node and marks the stateful pod as Failed, allowing its controller to recreate it. This prevents pods from getting stuck indefinitely in the `ContainerCreating` state. ([#132933](https://github.com/kubernetes/kubernetes/pull/132933), [@torredil](https://github.com/torredil)) [SIG Apps, Node, Storage and Testing] -- Kubelet now reports a hash of the credential provider configuration via the `kubelet_credential_provider_config_info` metric. The hash is exposed in the `hash` label. ([#133016](https://github.com/kubernetes/kubernetes/pull/133016), [@aramase](https://github.com/aramase)) [SIG API Machinery and Auth] -- Memory limits can now be decreased with a NotRequired resize restart policy. When decreasing memory limits, perform a best-effort check to prevent limits from decreasing below usage and triggering an OOM-kill. ([#133012](https://github.com/kubernetes/kubernetes/pull/133012), [@tallclair](https://github.com/tallclair)) [SIG Apps, Node and Testing] -- Move Recover from volume expansion failure GA ([#132662](https://github.com/kubernetes/kubernetes/pull/132662), [@gnufied](https://github.com/gnufied)) [SIG Apps, Auth, Node, Storage and Testing] -- PodLifecycleSleepAction is graduated to GA ([#132595](https://github.com/kubernetes/kubernetes/pull/132595), [@AxeZhan](https://github.com/AxeZhan)) [SIG Apps, Node and Testing] -- Prevents any type of CPU/Memory alignment or hint generation with the Topology manager from the CPU or Memory manager when Pod Level resources are used in the pod spec. ([#133279](https://github.com/kubernetes/kubernetes/pull/133279), [@ffromani](https://github.com/ffromani)) [SIG Node and Testing] -- Promoted Linux node pressure stall information (PSI) metrics to beta. ([#132822](https://github.com/kubernetes/kubernetes/pull/132822), [@roycaihw](https://github.com/roycaihw)) [SIG Node] -- Start recording metrics for in-place pod resize. ([#132903](https://github.com/kubernetes/kubernetes/pull/132903), [@natasha41575](https://github.com/natasha41575)) [SIG Node] -- The scheduler no longer clears the `nominatedNodeName` field for Pods. External components, such as Cluster Autoscaler and Karpenter, are responsible for managing this field when needed. ([#133276](https://github.com/kubernetes/kubernetes/pull/133276), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] -- The validation in the CertificateSigningRequest `/status` and `/approval` subresource has been migrated to declarative validation. - If the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate is enabled, declarative validation is the primary source of errors for migrated fields. ([#133068](https://github.com/kubernetes/kubernetes/pull/133068), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery and Auth] -- This will promote the `KubeletPodResourcesDynamicResources` and `KubeletPodResourcesGet` feature gates to Beta which will be enabled by default if DRA goes to GA. ([#132940](https://github.com/kubernetes/kubernetes/pull/132940), [@guptaNswati](https://github.com/guptaNswati)) -- Update pause version to registry.k8s.io/pause:3.10.1 ([#130713](https://github.com/kubernetes/kubernetes/pull/130713), [@ArkaSaha30](https://github.com/ArkaSaha30)) [SIG Cluster Lifecycle, Node, Scheduling and Testing] -- Use DRA API version to "v1" in "deviceattribute" package in "k8s.io/dynamic-resource-allocation" module ([#133164](https://github.com/kubernetes/kubernetes/pull/133164), [@everpeace](https://github.com/everpeace)) [SIG Node] -- When proxying to an aggregated API server, kube-apiserver now uses the - EndpointSlices of the `service` indicated by the `APIServer`, rather than - using Endpoints. - - If you are using the aggregated API server feature, and you are writing out - the endpoints for it by hand (rather than letting kube-controller-manager - generate Endpoints and EndpointSlices for it automatically based on the - Service definition), then you should write out an EndpointSlice object rather - than (or in addition to) an Endpoints object. ([#129837](https://github.com/kubernetes/kubernetes/pull/129837), [@danwinship](https://github.com/danwinship)) [SIG API Machinery, Network and Testing] -- Whenever a pod is successfully bound to a node, the kube-apiserver now clears the pod's `nominatedNodeName` field. This prevents stale information from affecting external scheduling components. ([#132443](https://github.com/kubernetes/kubernetes/pull/132443), [@utam0k](https://github.com/utam0k)) [SIG Apps, Node, Scheduling and Testing] - -### Failing Test - -- DRA driver helper: fixed handling of apiserver restart when running on a Kubernetes version which does not support the resource.k8s.io version used by the DRA driver. ([#133076](https://github.com/kubernetes/kubernetes/pull/133076), [@pohly](https://github.com/pohly)) [SIG Node and Testing] - -### Bug or Regression - -- Fix bug that prevents the alpha feature PodTopologyLabelAdmission to not work due to checking for the incorrect label key when copying topology labels. This bug delays the graduation of the feature to beta by an additional release to allow time for meaningful feedback. ([#132462](https://github.com/kubernetes/kubernetes/pull/132462), [@munnerz](https://github.com/munnerz)) [SIG Node] -- Fix runtime cost estimation for x-int-or-string custom resource schemas with maximum lengths ([#132837](https://github.com/kubernetes/kubernetes/pull/132837), [@JoelSpeed](https://github.com/JoelSpeed)) [SIG API Machinery] -- Fixed a bug that the async preemption feature keeps preemptor pods unnecessarily in the queue. ([#133167](https://github.com/kubernetes/kubernetes/pull/133167), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling] -- Kubeadm: fix a bug where the default args for etcd are not correct when a local etcd image is used and the etcd version is less than 3.6.0. ([#133023](https://github.com/kubernetes/kubernetes/pull/133023), [@carlory](https://github.com/carlory)) [SIG Cluster Lifecycle] -- Pods can't mix the usage of user-namespaces (`hostUsers: false`) and volumeDevices. Kubernetes now returns an error in this case. ([#132868](https://github.com/kubernetes/kubernetes/pull/132868), [@rata](https://github.com/rata)) [SIG Apps] -- ReplicaSets and Deployments should always count `.status.availableReplicas` at the correct time without a delay. This results in faster reconciliation of Deployment conditions and faster, unblocked Deployment rollouts. ([#132121](https://github.com/kubernetes/kubernetes/pull/132121), [@atiratree](https://github.com/atiratree)) [SIG Apps] -- The `baseline` and `restricted` pod security admission levels now block setting the `host` field on probe and lifecycle handlers ([#125271](https://github.com/kubernetes/kubernetes/pull/125271), [@tssurya](https://github.com/tssurya)) [SIG Auth, Node and Testing] -- The garbage collection controller no longer races with changes to ownerReferences when deleting orphaned objects. ([#132632](https://github.com/kubernetes/kubernetes/pull/132632), [@sdowell](https://github.com/sdowell)) [SIG API Machinery and Apps] - -### Other (Cleanup or Flake) - -- APIVersion fields of the HorizontalPodAutoscaler API are now validated to ensure created API objects function properly ([#132537](https://github.com/kubernetes/kubernetes/pull/132537), [@lalitc375](https://github.com/lalitc375)) [SIG Etcd and Testing] -- Added support for encoding and decoding types that implement the standard library interfaces json.Marshaler, json.Unmarshaler, encoding.TextMarshaler, or encoding.TextUnmarshaler to and from CBOR by transcoding. ([#132935](https://github.com/kubernetes/kubernetes/pull/132935), [@benluddy](https://github.com/benluddy)) [SIG API Machinery] -- Kubeadm: instead of passing the etcd flag --experimental-initial-corrupt-check, set the InitialCorruptCheck=true etcd feature gate, and instead of passing the --experimental-watch-progress-notify-interval flag, pass its graduated variant --watch-progress-notify-interval. ([#132838](https://github.com/kubernetes/kubernetes/pull/132838), [@AwesomePatrol](https://github.com/AwesomePatrol)) [SIG Cluster Lifecycle] -- Migrate Memory Manager to contextual logging ([#130727](https://github.com/kubernetes/kubernetes/pull/130727), [@swatisehgal](https://github.com/swatisehgal)) [SIG Node] -- Migrate pkg/kubelet/volumemanager to contextual logging ([#131306](https://github.com/kubernetes/kubernetes/pull/131306), [@Chulong-Li](https://github.com/Chulong-Li)) [SIG Node] -- Migrate pkg/kubelet/winstats to contextual logging ([#131001](https://github.com/kubernetes/kubernetes/pull/131001), [@Chulong-Li](https://github.com/Chulong-Li)) [SIG Node] -- Removed deprecated gogo protocol definitions from `k8s.io/kms/apis` in favor of `google.golang.org/protobuf`. ([#132833](https://github.com/kubernetes/kubernetes/pull/132833), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery, Auth and Testing] -- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/deviceplugin` in favor of `google.golang.org/protobuf`. ([#133028](https://github.com/kubernetes/kubernetes/pull/133028), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node and Testing] -- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/podresources` in favor of `google.golang.org/protobuf`. ([#133027](https://github.com/kubernetes/kubernetes/pull/133027), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node and Testing] -- Removed general avaliable feature-gate DevicePluginCDIDevices ([#132083](https://github.com/kubernetes/kubernetes/pull/132083), [@carlory](https://github.com/carlory)) [SIG Node and Testing] -- Replaces timer ptr helper function with the "k8s.io/utils/ptr" implementations. ([#133030](https://github.com/kubernetes/kubernetes/pull/133030), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- The deprecated `LegacySidecarContainers` feature gate is completely removed. ([#131463](https://github.com/kubernetes/kubernetes/pull/131463), [@gjkim42](https://github.com/gjkim42)) [SIG Node and Testing] -- Types: NodeInfo, PodInfo, QueuedPodInfo, PodResource, AffinityTerm, WeightedAffinityTerm, Resource, ImageStateSummary, ProtocolPort and HostPortInfo moved from pkg/scheduler/framework to staging repo. - Users should update import path for these types from "k8s.io/kubernetes/pkg/scheduler/framework" to "k8s.io/kube-scheduler/framework" and update use of fields (to use getter/setter functions instead) where needed. ([#132457](https://github.com/kubernetes/kubernetes/pull/132457), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Updates the etcd client library to v3.6.4 ([#133226](https://github.com/kubernetes/kubernetes/pull/133226), [@ivanvc](https://github.com/ivanvc)) [SIG API Machinery, Auth, Cloud Provider and Node] -- Upgrades functionality of `kubectl kustomize` as described at https://github.com/kubernetes-sigs/kustomize/releases/tag/kustomize%2Fv5.7.0 ([#132593](https://github.com/kubernetes/kubernetes/pull/132593), [@koba1t](https://github.com/koba1t)) [SIG CLI] - -## Dependencies - -### Added -_Nothing has changed._ - -### Changed -- cel.dev/expr: v0.23.1 → v0.24.0 -- github.com/fxamacker/cbor/v2: [v2.8.0 → v2.9.0](https://github.com/fxamacker/cbor/compare/v2.8.0...v2.9.0) -- github.com/google/cel-go: [v0.25.0 → v0.26.0](https://github.com/google/cel-go/compare/v0.25.0...v0.26.0) -- go.etcd.io/bbolt: v1.4.0 → v1.4.2 -- go.etcd.io/etcd/api/v3: v3.6.1 → v3.6.4 -- go.etcd.io/etcd/client/pkg/v3: v3.6.1 → v3.6.4 -- go.etcd.io/etcd/client/v3: v3.6.1 → v3.6.4 -- go.etcd.io/etcd/pkg/v3: v3.6.1 → v3.6.4 -- go.etcd.io/etcd/server/v3: v3.6.1 → v3.6.4 -- sigs.k8s.io/kustomize/api: v0.19.0 → v0.20.1 -- sigs.k8s.io/kustomize/cmd/config: v0.19.0 → v0.20.1 -- sigs.k8s.io/kustomize/kustomize/v5: v5.6.0 → v5.7.1 -- sigs.k8s.io/kustomize/kyaml: v0.19.0 → v0.20.1 -- sigs.k8s.io/structured-merge-diff/v6: v6.2.0 → v6.3.0 -- sigs.k8s.io/yaml: v1.5.0 → v1.6.0 - -### Removed -- github.com/google/shlex: [e7afc7f](https://github.com/google/shlex/tree/e7afc7f) - - - -# v1.34.0-beta.0 - - -## Downloads for v1.34.0-beta.0 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes.tar.gz) | e1a1cf79f95354bae349afa992f72cf8cb23aa9a016f67599de1f0c31572a00cd84f541163d0da3205ecfe421901a88dc2c9012cec91d45fa2f094d524059f92 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-src.tar.gz) | 7e2c9837dd9be43df835d999024d516d52d211ee7e65f995da8e6c45442c8a8b6e5bc3e13a9279fc401c58b3ad1ba2b0b37abba3719e0605dfb5cb5c752d7df7 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-darwin-amd64.tar.gz) | 1a3944812f26c37de6418f84d14e97366a1d2e268d8d61619f98f92778f3f3a9e30e4fd092ea0963ee19524284815803511e3d143c9f1b7df77f06728eddcefd -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-darwin-arm64.tar.gz) | 01bcf3e380e9b18e7db316c0a7968b9293ff0cee6bd6395f8b3a8fcfbd9bc660b3016cfa636498c28d35a0e8a221f56303bd34b136d044df2356f3085aa4e613 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-386.tar.gz) | 847526c7c2d2559f16ad1f6172d07590b4f35051a7bcf741c98067ace09fc92c52241f74a8c1d7ad1f4b713b26d8abc7059b47d97f4a8d9afc87d465b837dfd4 -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-amd64.tar.gz) | 260d78b743af5e7a6563cf26df7a4a4e75987f1bce96de3cec020d47f1a2586a39f3058cc1668a0b77266bb131490c74c55eaf669766918c8379e3c9818abebe -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-arm.tar.gz) | f4dcc3597f2e005b51c4f3fc8323e119582fd00626ddaea6f2602810fd64fb65d1c1a795519d458b2c74ef5bd52467e6cd77b01972e858bb97d12f4ef2c81839 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-arm64.tar.gz) | 4cc18be405d27f797ccd93b2f3ae0fe985450a0cf6f35e023c91e4a116b8443e32ba99e07bbc93c8dc4d9739c5adbb888cbc16ba457e362975e907057d0f38c1 -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-ppc64le.tar.gz) | 06eca6eb5dc82304566fc7194f1ae6f002a70dd031357608bbf65e9449840dcb55b37b1c61ff13e40f0eb95a0456bb6e5d692b14f806cd7e694ef71cb720bfb1 -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-linux-s390x.tar.gz) | ed6db8acb534c557e3619628b78c1de5abcb31bda04e418296acc4fde54e23bba1ee42b4db9daefdf5622b09e3c9d4916461b85da10058d822251ac3da2eebca -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-windows-386.tar.gz) | 0302f1dea8c321f254b9aeb87882c82b28a4be74b4718f73840769e06c21a4a240d285ec89d94657522e49bd7550eda44a8e7312d83198c4b4f60990609beaae -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-windows-amd64.tar.gz) | 28dcf914521f31ed11d258fe1ff516eac9f7e1ed317bc55a816a2bca2ef41ce18140c296ea0c22e1a3808f82979ce8970e91951a982c33dd18e3fedb840ca4ad -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-client-windows-arm64.tar.gz) | ed50434e96f2fd80abaf3b9fa6befa96f829c086ac6b87d0d9f6ce9d6d3e10a22eb17928902b42b95ad4709a936e791d189b338af46fbe91d5391fde7c1f2904 - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-server-linux-amd64.tar.gz) | 2862b8ed25f52542558fe48a6a584b02644a731decec878cfa0cee00173476f354d70a04efb84d084b87fe303291092d06e795e42e13a40c339699982a90044a -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-server-linux-arm64.tar.gz) | 1c00a6559f4f6c6190fe2265fb88cad4ac448eb3223dbd809976e3c85139d04b9cc02b4a9b80e9b42a2e4ee4a7a03a7a303ced49bc9673bff7be7cde7bb5f7a5 -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-server-linux-ppc64le.tar.gz) | 7a998922d3fff36914ee690a5937d7b592f1916f68f7a31311065b25e7035cd38572df062e90680d56299b93be278c2fa24a370547270c07add274cf4a420d2f -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-server-linux-s390x.tar.gz) | 555b5690e99d0470ea7ca1bc4aebfda68a1126859962876db897b3024d5d7e352a3beeae4f2f3cba28a0d1b3c6edcf7094395492ff36fbc7d2d7a1e87ebb5fca - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-node-linux-amd64.tar.gz) | 4b029d2f1022c4fd84ad1afaeeff9ae4fd80593c90f3f30a633df04bde68fac182c72bd906575b779eff01cc2e7d18884d9b5b0a3259a02e3131976a4339d1e1 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-node-linux-arm64.tar.gz) | c65b44be119997d321d13d6f9d08e42b1576fb9515cbf646c730f72e4e80a47afa1e59ea55cf8a8de1aa93a9db586ecb7101b2f59633460f4a4381ded987051b -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-node-linux-ppc64le.tar.gz) | 837442a3311c2382b417e2d8cbf9638f9abc22f8584519becd44e9a161ef2cecee686a76977391f2c20b0477d5417d657ec29b9f0ab81e059a64f9566065f37b -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-node-linux-s390x.tar.gz) | 3c8232cd07d8869258cc4a7793fee524ec26847d32c4c6efe966946b81df6e36450acbfcbe199296b2ad79201875d00e7a8af8ceacc2c9681fdae9b4a11c2c0e -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-beta.0/kubernetes-node-windows-amd64.tar.gz) | 768c4cd582f4b708451d5f3fdacf048de7550251e468a9e255f1c5180602d7abca5f86f22a16089309e35c0f5eee18c9133cebe24830461e3471bc180efc3769 - -### 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.0-beta.0](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.0-beta.0](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.0-beta.0](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.0-beta.0](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.0-beta.0](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.0-beta.0](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.0-alpha.3 - -## Changes by Kind - -### API Change - -- Added `tokenAttributes.cacheType` field to v1 credential provider config. This field is required to be set to either ServiceAccount or Token when configuring a provider that uses service account to fetch registry credentials. ([#132617](https://github.com/kubernetes/kubernetes/pull/132617), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] -- JWT authenticators specified via the `AuthenticationConfiguration.jwt` array can now optionally specify either the `controlplane` or `cluster` egress selector by setting the `issuer.egressSelectorType` field. When unset, the prior behavior of using no egress selector is retained. The StructuredAuthenticationConfigurationEgressSelector beta feature (default on) must be enabled to use this functionality. ([#132768](https://github.com/kubernetes/kubernetes/pull/132768), [@enj](https://github.com/enj)) [SIG API Machinery, Auth and Testing] -- Promoted the `KubeletTracing` feature gate to GA. ([#132341](https://github.com/kubernetes/kubernetes/pull/132341), [@dashpole](https://github.com/dashpole)) [SIG Instrumentation and Node] -- Replaces boolPtrFn helper functions with the "k8s.io/utils/ptr" implementation. ([#132907](https://github.com/kubernetes/kubernetes/pull/132907), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Architecture] -- Simplied validation error message for invalid fields by removing redundant field name. ([#132513](https://github.com/kubernetes/kubernetes/pull/132513), [@xiaoweim](https://github.com/xiaoweim)) [SIG API Machinery, Apps, Auth, Node and Scheduling] -- The `AuthorizeWithSelectors` and `AuthorizeNodeWithSelectors` feature gates are promoted to stable and locked on. ([#132656](https://github.com/kubernetes/kubernetes/pull/132656), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth and Testing] - -### Feature - -- Add DetectCacheInconsistency feature gate that allows apiserver to periodically compare consistency between cache and etcd. Inconsistency is reported to `apiserver_storage_consistency_checks_total` metric and results in cache snapshots being purged. ([#132884](https://github.com/kubernetes/kubernetes/pull/132884), [@serathius](https://github.com/serathius)) [SIG API Machinery, Instrumentation and Testing] -- Add SizeBasedListCostEstimate feature gate, enabled by default, changing method of assigning APF seats to LIST request. Assign one seat per 100KB of data loaded to memory at once to handle LIST request. ([#132932](https://github.com/kubernetes/kubernetes/pull/132932), [@serathius](https://github.com/serathius)) [SIG API Machinery] -- Add warning on use of alpha metrics with emulated versions. ([#132276](https://github.com/kubernetes/kubernetes/pull/132276), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery and Architecture] -- Compact snapshots in watch cache based on etcd compaction ([#132876](https://github.com/kubernetes/kubernetes/pull/132876), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- Graduate `ConsistentListFromCache` to GA ([#132645](https://github.com/kubernetes/kubernetes/pull/132645), [@serathius](https://github.com/serathius)) [SIG API Machinery] -- Kubeadm: started using a named port 'probe-port' for all probes in the static pod manifests for kube-apiserver, kube-controller-manager, kube-scheduler and etc. If you have previously patched the port values in probes with kubeadm patches, you must now also patch the named port value in the pod container under 'ports'. ([#132776](https://github.com/kubernetes/kubernetes/pull/132776), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] -- Kubernetes is now built using Go 1.24.5 ([#132896](https://github.com/kubernetes/kubernetes/pull/132896), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- New PreBindPreFlight function is added to PreBindPlugin interface. In-tree PreBind plugins now implement PreBindPreFlight function. ([#132391](https://github.com/kubernetes/kubernetes/pull/132391), [@sanposhiho](https://github.com/sanposhiho)) [SIG Node, Scheduling, Storage and Testing] -- Prioritize resize requests by priorityClass and qos class when there is not enough room on the node to accept all the resize requests. ([#132342](https://github.com/kubernetes/kubernetes/pull/132342), [@natasha41575](https://github.com/natasha41575)) [SIG Node and Testing] -- Promote Ordered Namespace Deletion to Conformance ([#132219](https://github.com/kubernetes/kubernetes/pull/132219), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture and Testing] - -### Bug or Regression - -- CLI: `kubectl get job` now displays the SuccessCriteriaMet status for the listed jobs. ([#132832](https://github.com/kubernetes/kubernetes/pull/132832), [@Goend](https://github.com/Goend)) [SIG Apps and CLI] -- Change the node-local podresources API endpoint to only consider of active pods. Because this fix changes a long-established behavior, users observing a regressions can use the KubeletPodResourcesListUseActivePods feature gate (default on) to restore the old behavior. Please file an issue if you encounter problems and have to use the Feature Gate. ([#132028](https://github.com/kubernetes/kubernetes/pull/132028), [@ffromani](https://github.com/ffromani)) [SIG Node and Testing] -- Fix kubelet token cache returning stale tokens when service accounts are recreated with the same name. The token cache is now UID-aware and the new `TokenRequestServiceAccountUIDValidation` feature gate (Beta, enabled by default) validates the TokenRequest UID when set matches the service account UID. ([#132803](https://github.com/kubernetes/kubernetes/pull/132803), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth, Node and Testing] -- Fixed a bug that caused duplicate validation when updating a DaemonSet. ([#132548](https://github.com/kubernetes/kubernetes/pull/132548), [@gavinkflam](https://github.com/gavinkflam)) [SIG Apps] -- Kube-proxy nftables now reject/drop traffic to service with no endpoints from filter chains at priority 0 (NF_IP_PRI_FILTER) ([#132456](https://github.com/kubernetes/kubernetes/pull/132456), [@aroradaman](https://github.com/aroradaman)) [SIG Network] -- When both InPlacePodVerticalScaling and PodObservedGenerationTracking feature gates are set, fix the `observedGeneration` field exposed in the pod resize conditions to more accurately reflect which pod generation is associated with the condition. ([#131157](https://github.com/kubernetes/kubernetes/pull/131157), [@natasha41575](https://github.com/natasha41575)) [SIG Node] -- Windows kube-proxy: ensures that Windows kube-proxy aligns with Linux behavior and correctly honors the EndpointSlice-provided port for internal traffic routing. ([#132647](https://github.com/kubernetes/kubernetes/pull/132647), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] - -### Other (Cleanup or Flake) - -- Kubeadm: instead of passing the etcd flag --experimental-initial-corrupt-check, set the InitialCorruptCheck=true etcd feature gate, and instead of passing the --experimental-watch-progress-notify-interval flag, pass its graduated variant --watch-progress-notify-interval. ([#132838](https://github.com/kubernetes/kubernetes/pull/132838), [@AwesomePatrol](https://github.com/AwesomePatrol)) [SIG Cluster Lifecycle] -- Masked off access to Linux thermal interrupt info in `/proc` and `/sys`. ([#131018](https://github.com/kubernetes/kubernetes/pull/131018), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node] -- NONW ([#132890](https://github.com/kubernetes/kubernetes/pull/132890), [@atiratree](https://github.com/atiratree)) [SIG Apps] -- Promoted two EndpointSlice tests to conformance, to require that service - proxy implementations are based on EndpointSlices rather than Endpoints. ([#132019](https://github.com/kubernetes/kubernetes/pull/132019), [@danwinship](https://github.com/danwinship)) [SIG Architecture, Network and Testing] -- Reduced excessive logging from the volume binding scheduler plugin by lowering verbosity of high-frequency messages from V(4) to V(5). ([#132840](https://github.com/kubernetes/kubernetes/pull/132840), [@ppmechlinski](https://github.com/ppmechlinski)) [SIG Autoscaling, Scheduling and Storage] - -## Dependencies - -### Added -- sigs.k8s.io/structured-merge-diff/v6: v6.2.0 - -### Changed -- k8s.io/kube-openapi: d90c4fd → f3f2b99 - -### Removed -- sigs.k8s.io/structured-merge-diff/v4: v4.7.0 - - - -# v1.34.0-alpha.3 - - -## Downloads for v1.34.0-alpha.3 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes.tar.gz) | ec76c311b4aa0bcc97d4a83e6586a14081129343721bf844f0907ec2e14cad1ba4d0db04b667de963043c0fd4b410f7fe90788c10f070fa3b8ad0aa340e2dc5f -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-src.tar.gz) | b99cf04b86438285c24872e6ec2fdc03998a95b88e502e457ea03fba01beb870ef34e57055f7a14a016ae102906a1ed32ea20ddada31c9c1fa467c47b203d1f9 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-darwin-amd64.tar.gz) | 5f2b298b4f1c27e06e79258b2ac840a36f70d46bd95b776d01bee89c1821b8a1138556224d4c23b8e582ca1676e0125bda8ebc93e8db0a92ede240efa169b01f -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-darwin-arm64.tar.gz) | 719d4d81d85cf7f73e6f461e17c1559768f4e084753d0b210603e920b2ee6d687350e7ba5ae0bfa160630c02159e2900936ebde03104050f2eb6906b24573694 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-386.tar.gz) | 07ec8bd3d5308431bb4cf17dc8937ad13b95a2aab35fa0389479776228cc0f47756d1791d2371a66fa8f045d1894ac6d0dec4e42a3f96e443ea961cd2e7477ee -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-amd64.tar.gz) | 5c4613fa4b8de852147a24e7c80894f1588e93023cff4bfe58725e2b141f5417662bcf837272c41eeaf8a91382eca3e6015b27cca099e516ba2e08214521279a -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-arm.tar.gz) | 1204c3f108b5e83a081b31af13d9e3185f0ff3c9547213ecbd854293b89661f5060c2b10a2c73d8bb6d5099438287dedbcdf88f33de6bc95a060e2d634d80652 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-arm64.tar.gz) | 763d07a3a3f69b42047686e81e6cc137f9ef4b7ab2f50cc7bfbb26b8a15e011549501893e5cb9b77de0008cc77631fd8f37d113c0f0ee6a17e6435da06c269a2 -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-ppc64le.tar.gz) | ad4f45f8402014da35a3ff1daa5cfaafedd2d7e579bff1a0a87f49647e2d8488eb2b791776de3e4d5ac25631f13ec9c0bc64e7e0edd9f9049619659b9ebf9305 -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-linux-s390x.tar.gz) | aba923f458d8f8d4c27b0144f40cac186663884447a5a67c20d2fb59d0c9ebd83e8d7480555365562e97c88992d28ffdd20378ce18412465a0356b2c20fa5dff -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-windows-386.tar.gz) | b5b1a854e0298f2f401627ecec5bd61fad8ccb7f77a42b5c34e6c0dc8f4f5e7485d13525f2775b160b15277d591d46c1633acddea60dd2b20949794f0f80a4e5 -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-windows-amd64.tar.gz) | 75b60210f1e6994abc4da9fceb23290438be81c094e27b2025c96addde4cbc034348dde0d50098db22aeceed3e3d6dd855d8d740d36e0f16932ca4ae537542d0 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-client-windows-arm64.tar.gz) | f997fa3ba6081273b46e6a71a98fcee06c0df36e045fd43eb38454b28dcf3863e8e5f053cc14057f9cfd53267ae611477f5410a2305d56b7a60a88f4c0cb36bd - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-server-linux-amd64.tar.gz) | 3313f4746bdfaf7bd86bd72d035c552ae800426f5546eb23b83bdb3178e378d3aa5c4a59bc2b5ce5d97755432879812a293f582ca1dec3733e95adc5a5c07524 -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-server-linux-arm64.tar.gz) | add5d69f2d48656649d1712c476a9de99ef2fcf4473d982b78b834e5ad544cf947a9fc35324552cd38e3824ea96194a81d76bc99111bfc725b9aa9212da8e88d -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-server-linux-ppc64le.tar.gz) | f2038f7382e660e8c97c4efa05adcb3d785ccd550597a50aa9d98e04e9bf1f29b5ac0c5d3d686f01870d64949dc43cf83e176c718fddc20f84ed38bd44f8ba54 -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-server-linux-s390x.tar.gz) | 077708405b4b22ebeaee8feeddbe1134374008129cc0cc40830434fa762f549f5950c1c2f76b72ddfab2ccc972e3325cb360a13acdbe54729a1eae0324b60b08 - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-node-linux-amd64.tar.gz) | 9c0a3e76311789bfbaa3d8e27c289e5b5ab142ab0269dd5922016d2e3e8be6ddffd60ba1a57d6deb2571e4826bec1aab81c98a973d633b263ac316275ee01251 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-node-linux-arm64.tar.gz) | 1289b5e39164eaac2acce143dbb389341966e06b8b0261eb0eb4eb774848dd34f35b78d22b56d1613d5a6801868881415af62aa15ff8b1cbedbec9cad0567591 -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-node-linux-ppc64le.tar.gz) | 235d060ddd4c3da0b58fdb64a6a25b03690b7eb9c2888201b86d269f29d2a4e70000cea9711dff472626e78b56e228b9ffd16ab89442c54991de44bc8c3d9344 -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-node-linux-s390x.tar.gz) | 8e9ca919e77e0ff226e92c036ac35ab284aa07fdd2e01b01bb4352d04acd567f40edc4caf934092cb3c1b04ac4ea281ef6681b6589d9e43ea25ae038381e95ad -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.3/kubernetes-node-windows-amd64.tar.gz) | 421cf6c68c5e0603f67dd44ac0d4d02c6feb3e60e9608268cdc4b498718c99b2cef4842fb7df60feaa99dc18d650164a0fcc31fbf896653ea4b6d126e0683d14 - -### 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.0-alpha.3](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.0-alpha.3](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.0-alpha.3](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.0-alpha.3](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.0-alpha.3](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.0-alpha.3](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.0-alpha.2 - -## Changes by Kind - -### API Change - -- DRA: the v1alpha4 kubelet gRPC API (added in 1.31, superseded in 1.32) is no longer supported. DRA drivers using the helper package from Kubernetes >= 1.32 use the v1beta1 API and continue to be supported. ([#132574](https://github.com/kubernetes/kubernetes/pull/132574), [@pohly](https://github.com/pohly)) [SIG Node] -- Deprecate StreamingConnectionIdleTimeout field of the kubelet config. ([#131992](https://github.com/kubernetes/kubernetes/pull/131992), [@lalitc375](https://github.com/lalitc375)) [SIG Node] -- Removed deprecated gogo protocol definitions from `k8s.io/cri-api` in favor of `google.golang.org/protobuf`. ([#128653](https://github.com/kubernetes/kubernetes/pull/128653), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery, Auth, Instrumentation, Node and Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the apiextensions-apiserver apiextensions. ([#132723](https://github.com/kubernetes/kubernetes/pull/132723), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the component-base. ([#132754](https://github.com/kubernetes/kubernetes/pull/132754), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Architecture, Instrumentation and Scheduling] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the kube-aggregator apiregistration. ([#132701](https://github.com/kubernetes/kubernetes/pull/132701), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaces Boolean-pointer-helper functions with the "k8s.io/utils/ptr" implementations. ([#132794](https://github.com/kubernetes/kubernetes/pull/132794), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Auth, CLI, Node and Testing] -- Replaces deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the apiserver (1/2). ([#132751](https://github.com/kubernetes/kubernetes/pull/132751), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Simplied validation error message for required fields by removing redundant messages. ([#132472](https://github.com/kubernetes/kubernetes/pull/132472), [@xiaoweim](https://github.com/xiaoweim)) [SIG API Machinery, Apps, Architecture, Auth, Cloud Provider, Network, Node and Storage] - -### Feature - -- Add configurable flags to kube-apiserver for coordinated leader election. ([#132433](https://github.com/kubernetes/kubernetes/pull/132433), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery and Testing] -- Add support for --cpu, --memory flag to kubectl autoscale, start deprecating --cpu-precent. ([#129373](https://github.com/kubernetes/kubernetes/pull/129373), [@googs1025](https://github.com/googs1025)) [SIG CLI] -- Added SizeBasedListCostEstimate feature gate that allows apiserver to estimate sizes of objects to calculate cost of LIST requests ([#132355](https://github.com/kubernetes/kubernetes/pull/132355), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] -- DRA kubelet: the kubelet now also cleans up ResourceSlices in some additional failure scenarios (driver gets removed forcibly or crashes and does not restart). ([#132058](https://github.com/kubernetes/kubernetes/pull/132058), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- Graduate `StreamingCollectionEncodingToJSON` and `StreamingCollectionEncodingToProtobuf` to GA ([#132648](https://github.com/kubernetes/kubernetes/pull/132648), [@serathius](https://github.com/serathius)) [SIG API Machinery] -- Kubeadm: graduated the kubeadm specific feature gate WaitForAllControlPlaneComponents to GA. The feature gate is now locked to always enabled and on node initialization kubeadm will perform a health check for all control plane components and not only the kube-apiserver. ([#132594](https://github.com/kubernetes/kubernetes/pull/132594), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] -- Static pods that reference API objects are now denied admission by the kubelet so that static pods would not be silently running even after the mirror pod creation fails. ([#131837](https://github.com/kubernetes/kubernetes/pull/131837), [@sreeram-venkitesh](https://github.com/sreeram-venkitesh)) [SIG Auth, Node and Testing] -- The new `dra_resource_claims_in_use` kubelet metrics informs about active ResourceClaims, overall and by driver. ([#131641](https://github.com/kubernetes/kubernetes/pull/131641), [@pohly](https://github.com/pohly)) [SIG Architecture, Instrumentation, Node and Testing] -- When `RelaxedServiceNameValidation` feature gate is enabled, the - names of new Services names are validation with `NameIsDNSLabel()`, - relaxing the pre-existing validation. ([#132339](https://github.com/kubernetes/kubernetes/pull/132339), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Apps, Network and Testing] - -### Failing Test - -- Fixed e2e test "[Driver: csi-hostpath] [Testpattern: Dynamic PV (filesystem volmode)] volumeLimits should support volume limits]" not to leak Pods and namespaces. ([#132674](https://github.com/kubernetes/kubernetes/pull/132674), [@jsafrane](https://github.com/jsafrane)) [SIG Storage and Testing] - -### Bug or Regression - -- Add podSpec validation for create StatefulSet ([#131790](https://github.com/kubernetes/kubernetes/pull/131790), [@chengjoey](https://github.com/chengjoey)) [SIG Apps, Etcd and Testing] -- Clarify help message of --ignore-not-found flag. Support --ignore-not-found in `watch` operation. ([#132542](https://github.com/kubernetes/kubernetes/pull/132542), [@gemmahou](https://github.com/gemmahou)) [SIG CLI] -- DRA drivers: the resource slice controller sometimes didn't react properly when kubelet or someone else deleted a recently created ResourceSlice. It incorrectly assumed that the ResourceSlice still exists and didn't recreate it. ([#132683](https://github.com/kubernetes/kubernetes/pull/132683), [@pohly](https://github.com/pohly)) [SIG Apps, Node and Testing] -- Ensure objects are transformed prior to storage in SharedInformers if a transformer is provided and `WatchList` is activated ([#131799](https://github.com/kubernetes/kubernetes/pull/131799), [@valerian-roche](https://github.com/valerian-roche)) [SIG API Machinery] -- Fix validation for Job with suspend=true, and completions=0 to set the Complete condition. ([#132614](https://github.com/kubernetes/kubernetes/pull/132614), [@mimowo](https://github.com/mimowo)) [SIG Apps and Testing] -- Fixed a bug that fails to create a replica set when a deployment name is too long. ([#132560](https://github.com/kubernetes/kubernetes/pull/132560), [@hdp617](https://github.com/hdp617)) [SIG API Machinery and Apps] -- Fixed the bug when swap related metrics were not available in `/metrics/resource` endpoint. ([#132065](https://github.com/kubernetes/kubernetes/pull/132065), [@yuanwang04](https://github.com/yuanwang04)) [SIG Node and Testing] -- Fixed the problem of validation error when specifying resource requirements at the container level for a resource not supported at the pod level. It implicitly interpreted the pod-level value as 0. ([#132551](https://github.com/kubernetes/kubernetes/pull/132551), [@chao-liang](https://github.com/chao-liang)) [SIG Apps] -- HPA status now displays memory metrics using Ki ([#132351](https://github.com/kubernetes/kubernetes/pull/132351), [@googs1025](https://github.com/googs1025)) [SIG Apps and Autoscaling] -- Removed defunct `make vet` target, please use `make lint` instead ([#132509](https://github.com/kubernetes/kubernetes/pull/132509), [@yongruilin](https://github.com/yongruilin)) [SIG Testing] -- Statefulset now respects minReadySeconds ([#130909](https://github.com/kubernetes/kubernetes/pull/130909), [@Edwinhr716](https://github.com/Edwinhr716)) [SIG Apps] - -### Other (Cleanup or Flake) - -- Removed deprecated gogo protocol definitions from `k8s.io/externaljwt` in favor of `google.golang.org/protobuf`. ([#132772](https://github.com/kubernetes/kubernetes/pull/132772), [@saschagrunert](https://github.com/saschagrunert)) [SIG Auth] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for ./test/e2e and ./test/utils. ([#132763](https://github.com/kubernetes/kubernetes/pull/132763), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Autoscaling and Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for ./test/e2e. ([#132764](https://github.com/kubernetes/kubernetes/pull/132764), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth, Network, Node, Storage and Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for ./test/e2e. ([#132765](https://github.com/kubernetes/kubernetes/pull/132765), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps, CLI and Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for ./test/integration ([#132762](https://github.com/kubernetes/kubernetes/pull/132762), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for apiextensions apiservers validation tests. ([#132726](https://github.com/kubernetes/kubernetes/pull/132726), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for apiextensions-apiserver pkg/controller. ([#132724](https://github.com/kubernetes/kubernetes/pull/132724), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for apiextensions-apiserver pkg/registry. ([#132725](https://github.com/kubernetes/kubernetes/pull/132725), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for pkg/apis (1/2). ([#132778](https://github.com/kubernetes/kubernetes/pull/132778), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps and Network] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for pkg/apis (2/2). ([#132779](https://github.com/kubernetes/kubernetes/pull/132779), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps, Auth and Storage] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for pkg/controller (1/2). ([#132781](https://github.com/kubernetes/kubernetes/pull/132781), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps and Network] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for pkg/controller (2/2). ([#132784](https://github.com/kubernetes/kubernetes/pull/132784), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery, Apps, Network, Node and Storage] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for pod-security-admission tests. ([#132741](https://github.com/kubernetes/kubernetes/pull/132741), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the apiextensions-apiservers integration tests. ([#132721](https://github.com/kubernetes/kubernetes/pull/132721), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the cli-runtime. ([#132750](https://github.com/kubernetes/kubernetes/pull/132750), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG CLI and Release] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the cloud-provider. ([#132720](https://github.com/kubernetes/kubernetes/pull/132720), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Cloud Provider and Network] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the components-helper of the apimachinery. ([#132413](https://github.com/kubernetes/kubernetes/pull/132413), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the controller-manager. ([#132753](https://github.com/kubernetes/kubernetes/pull/132753), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Cloud Provider] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the csr. ([#132699](https://github.com/kubernetes/kubernetes/pull/132699), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the e2e_node. ([#132755](https://github.com/kubernetes/kubernetes/pull/132755), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Node and Testing] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the kubeapiserver. ([#132529](https://github.com/kubernetes/kubernetes/pull/132529), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Architecture] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the pkg/security and plugin/pkg. ([#132777](https://github.com/kubernetes/kubernetes/pull/132777), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth, Node and Release] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the pod-security-admission admissiontests. ([#132742](https://github.com/kubernetes/kubernetes/pull/132742), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the pod-security-admission policy. ([#132743](https://github.com/kubernetes/kubernetes/pull/132743), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth] -- Replaced deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the reflector. ([#132698](https://github.com/kubernetes/kubernetes/pull/132698), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery] -- Replaces deprecated package 'k8s.io/utils/pointer' with 'k8s.io/utils/ptr' for the apiserver (2/2). ([#132752](https://github.com/kubernetes/kubernetes/pull/132752), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Auth] -- Replaces toPtr helper functions with the "k8s.io/utils/ptr" implementations. ([#132806](https://github.com/kubernetes/kubernetes/pull/132806), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps, Testing and Windows] -- Types: ClusterEvent, ActionType, EventResource, ClusterEventWithHint, QueueingHint and QueueingHintFn moved from pkg/scheduler/framework to k8s.io/kube-scheduler/framework. ([#132190](https://github.com/kubernetes/kubernetes/pull/132190), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Types: Code and Status moved from pkg/scheduler/framework to staging repo. - Users should update import path for these types from "k8s.io/kubernetes/pkg/scheduler/framework" to "k8s.io/kube-scheduler/framework" ([#132087](https://github.com/kubernetes/kubernetes/pull/132087), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Update etcd version to v3.6.1 ([#132284](https://github.com/kubernetes/kubernetes/pull/132284), [@ArkaSaha30](https://github.com/ArkaSaha30)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] - -## Dependencies - -### Added -- go.yaml.in/yaml/v2: v2.4.2 -- go.yaml.in/yaml/v3: v3.0.4 - -### Changed -- github.com/emicklei/go-restful/v3: [v3.11.0 → v3.12.2](https://github.com/emicklei/go-restful/compare/v3.11.0...v3.12.2) -- github.com/google/gnostic-models: [v0.6.9 → v0.7.0](https://github.com/google/gnostic-models/compare/v0.6.9...v0.7.0) -- k8s.io/kube-openapi: 8b98d1e → d90c4fd -- sigs.k8s.io/json: 9aa6b5e → cfa47c3 -- sigs.k8s.io/yaml: v1.4.0 → v1.5.0 - -### Removed -_Nothing has changed._ - - - -# v1.34.0-alpha.2 - - -## Downloads for v1.34.0-alpha.2 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes.tar.gz) | 566db0b881557117fd7038bb5f25c46c727d2cc6a7cf3de0afc720eeeecfce947ae0e1b5162173a3ebfb915cfcc2c05fe8ab61db4551ac882a2756ad333d6337 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-src.tar.gz) | 3ccccf95776d0639455cead6d74a04e1af8f244915c583213b70f688ffd0cb291752da48589134eac5392ff1f6fb5046803d1e35f70475bcf74ded85c587df49 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-darwin-amd64.tar.gz) | 058f6b47787adabfbb191ef80888633cddf5e2e36be6bb113da7db2c239c2691ad5467d381b09ca78bf9c54397a7eb0d54f2025ba7314c504eee4537787982b1 -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-darwin-arm64.tar.gz) | 1e22f2b5c699e991daa282aaa1475d37e1614e4d90022dadc205b64c988c5050005a2347d0e93c9b0804c0db1fd0eb1f8eb4f86a0811638ccd9324cda95265fa -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-386.tar.gz) | dacff605a6be45b4844b5120e420aedeea422297de1c9d5b5bc5926cc730efdc13f9881c75cb346159cb8a4e0a4364070299ffcc41494dbdd8ece6f698238658 -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-amd64.tar.gz) | 38f5c80ad4cf1c8e422d5ac54cf6e5ea93425bd4fe4dd8d9ac011734e2b187769f74da749240bea1cc3a850ea6530dcbc27979af8fc9d86b3ec3299362c54e03 -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-arm.tar.gz) | e8e116b2603e961d6090da8755d61c895a5ec7e9b6bf0bfc52a6a2b45c2111c73f7c30496dfdc624778c9ce74aa116206c0b3adcc41b046d06d8301a55218679 -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-arm64.tar.gz) | ece12bddbae26f6d63e39482985a43429b768d82bc6c1b523724c134d98f52ae41c64f66d267d53400566bf0428021228c9cf9b0b663399ab27c08304bbe193f -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-ppc64le.tar.gz) | 4346923ea8eae6e51c07fa53a6a6f72d75ca6a50db5ae255c9902f4bd7af0a1cde359f9d6c2a84253c74e4d32f32ee81abe8b1dfaabb0206f871c57ba1eacb73 -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-linux-s390x.tar.gz) | e7f93440b0497bab07db1f5bf70be61148abe555a8aa83712201128056a0e53c0273a7269ba92c65af0095bb9e69d3bfe85359720969bca1399d21e0b04b1264 -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-windows-386.tar.gz) | 5a43893f34cac36608a7817c1116c43b71411ef75d71188886672941db7d8080efcb94e183b0beadc852b36b12986eb356bdde4c4a7729e284e214ba8cf43fea -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-windows-amd64.tar.gz) | 52f3514824ab0a152eaf588722f56e6d12366ecf8479e1cd11f0e878ed7c9b0b5ec528cdb7dd0f03273eef704adeaa3cce3918e89bd7a4c15480130aa5c6b5f3 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-client-windows-arm64.tar.gz) | 5f31dfc54626f31feff6373a7282cf624779a79b2178f0d7ff4e977652c5f8bc2b2c64de1b6db22eda9c563a4980b3b72b134ff2a1743a5b196ab3eeb6f5e452 - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-server-linux-amd64.tar.gz) | b13750ef0157384cf353ef6a4471fd17706c3bf3bd7bed2c84efc57f8863f3c7306a09813d8788fcd97d0e7e0929f4c136e2ac047c30fef2c45d4fb3d0bbe8ff -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-server-linux-arm64.tar.gz) | 84d004c4df9c46a280abfca2af02d5601d07fa8e1355b4ebfc2dcc069829804650a1097f97254c4f4ad0423b7f4828c76dfd2a56348aa1339f5518bbb9257c8e -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-server-linux-ppc64le.tar.gz) | f1cb4b333fc9bc696a3a75b4b0a846fe9f207c79fcfac438f2d3e3a709d23039c3f1507ea6e03dff2b5a4ef737052c613d354efe3b034384544ebf99551be7ea -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-server-linux-s390x.tar.gz) | ded6953d4d2b04f589a24f5e6e21aa3630d9a12f5562d8c8e6301660b1fa04782523500d74bb5399a8cb0d6102546bd1000591c3dd8464d98a3d3399576a20e7 - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-node-linux-amd64.tar.gz) | e4e4e2ac9acb4d36aded75ee4e841947b8eec2e66b08d11b01b662c5372be51cd746b9a87248a03be45c49de9aa53a31904b38a3e1253f0aaecc5e5b774cb4d7 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-node-linux-arm64.tar.gz) | 40318c61e6b060c18d2cd80143d83bab9178275f099e65b82161334eb9970ec9151b581654151799532564c92c4a3730abeb00a9e88ddcfddd66ed69d09c9921 -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-node-linux-ppc64le.tar.gz) | 778ca04559776b3e03537e13ff9f2136ae7fdb71c2a130e9734761cb2ad278f1af9e7285a132f2f77f49566f0c302ef8ca7f3694f84e48c93f5585236718cd8e -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-node-linux-s390x.tar.gz) | b6b0865359b6c767b233374918263d05cfd8fb52130b67ca7db2dcc01df119efbff89041a6310dbed77c96aa8faeb5a50bd1c184ccd9e5441eb09c1cb6df8e03 -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.2/kubernetes-node-windows-amd64.tar.gz) | 9374a3ec4ea2d417a63fef4809e982b9cc62f98ef67cd0bcabb7b673f1efaa82c66724bf605e658d0905cf9ee61f61f1ab00a077a330ee1e4197c06c582d9a37 - -### 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.0-alpha.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.0-alpha.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.0-alpha.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.0-alpha.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.0-alpha.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.0-alpha.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.0-alpha.1 - -## Changes by Kind - -### Deprecation - -- Apimachinery: deprecated MessageCountMap and CreateAggregateFromMessageCountMap ([#132376](https://github.com/kubernetes/kubernetes/pull/132376), [@tico88612](https://github.com/tico88612)) [SIG API Machinery] - -### API Change - -- Add a `runtime.ApplyConfiguration` interface that is implemented by all generated applyconfigs ([#132194](https://github.com/kubernetes/kubernetes/pull/132194), [@alvaroaleman](https://github.com/alvaroaleman)) [SIG API Machinery and Instrumentation] -- Added omitempty and opt tag to the API v1beta2 AdminAccess type in the DeviceRequestAllocationResult struct. ([#132338](https://github.com/kubernetes/kubernetes/pull/132338), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Auth] -- Introduces OpenAPI format support for `k8s-short-name` and `k8s-long-name`. ([#132504](https://github.com/kubernetes/kubernetes/pull/132504), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] -- Promoted Job Pod Replacement Policy to general availability. The `JobPodReplacementPolicy` feature gate is now locked to true, and will be removed in a future release of Kubernetes. ([#132173](https://github.com/kubernetes/kubernetes/pull/132173), [@dejanzele](https://github.com/dejanzele)) [SIG Apps and Testing] -- This PR corrects that documentation, making it clear to users that podSelector is optional and describes its default behavior. ([#131354](https://github.com/kubernetes/kubernetes/pull/131354), [@tomoish](https://github.com/tomoish)) [SIG Network] - -### Feature - -- Added a delay to node updates after kubelet startup. A random offset, based on the configured `nodeStatusReportFrequency`, helps spread the traffic and load (due to node status updates) more evenly over time. The initial status update can be up to 50% earlier or 50% later than the regular schedule. ([#130919](https://github.com/kubernetes/kubernetes/pull/130919), [@mengqiy](https://github.com/mengqiy)) [SIG Node] -- Included namespace in the output of the kubectl delete for clearer identification of resources. ([#126619](https://github.com/kubernetes/kubernetes/pull/126619), [@totegamma](https://github.com/totegamma)) [SIG CLI] -- Kube-apiserver: each unique set of etcd server overrides specified with `--etcd-servers-overrides` now surface health checks named `etcd-override-` and `etcd-override-readiness-`. These checks are still excluded by `?exclude=etcd` and `?exclude=etcd-readiness` directives. ([#129438](https://github.com/kubernetes/kubernetes/pull/129438), [@pacoxu](https://github.com/pacoxu)) [SIG API Machinery and Testing] - -### Bug or Regression - -- Fix regression introduced in 1.33 - where some Paginated LIST calls are falling back to etcd instead of serving from cache. ([#132244](https://github.com/kubernetes/kubernetes/pull/132244), [@hakuna-matatah](https://github.com/hakuna-matatah)) [SIG API Machinery] -- Fixed API response for StorageClassList queries and returns a graceful error message, if the provided ResourceVersion is too large. ([#132374](https://github.com/kubernetes/kubernetes/pull/132374), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG API Machinery and Etcd] -- Fixed an issue which allowed Custom Resources to be created with Server-Side Apply even when its CustomResourceDefinition was terminating. ([#132467](https://github.com/kubernetes/kubernetes/pull/132467), [@sdowell](https://github.com/sdowell)) [SIG API Machinery] -- Removed the deprecated flag '--wait-interval' for the ip6tables-legacy-restore binary. ([#132352](https://github.com/kubernetes/kubernetes/pull/132352), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Network] - -### Other (Cleanup or Flake) - -- Conntrack reconciler now considers service's target port during cleanup of stale flow entries. ([#130542](https://github.com/kubernetes/kubernetes/pull/130542), [@aroradaman](https://github.com/aroradaman)) [SIG Network] -- Job controller uses controller UID index for pod lookups. ([#132305](https://github.com/kubernetes/kubernetes/pull/132305), [@xigang](https://github.com/xigang)) [SIG Apps] -- Removed the deprecated `--register-schedulable` command line argument from the kubelet. ([#122384](https://github.com/kubernetes/kubernetes/pull/122384), [@carlory](https://github.com/carlory)) [SIG Cloud Provider, Node and Scalability] -- Removes the `kubernetes.io/initial-events-list-blueprint` annotation from the synthetic "Bookmark" event for the watch stream requests. ([#132326](https://github.com/kubernetes/kubernetes/pull/132326), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery] - -## Dependencies - -### Added -- go.yaml.in/yaml/v2: v2.4.2 -- go.yaml.in/yaml/v3: v3.0.3 - -### Changed -- k8s.io/kube-openapi: c8a335a → 8b98d1e -- sigs.k8s.io/yaml: v1.4.0 → v1.5.0 - -### Removed -_Nothing has changed._ - - - -# v1.34.0-alpha.1 - - -## Downloads for v1.34.0-alpha.1 - - - -### Source Code - -filename | sha512 hash --------- | ----------- -[kubernetes.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes.tar.gz) | 4125206915e9f0cd7bffd77021f210901bade4747d84855c8210922c82e2085628a05b81cef137e347b16a05828f99ac2a27a8f8f19a14397011031454736ea0 -[kubernetes-src.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-src.tar.gz) | c1dfe0a1df556adcad5881a7960da5348feacc23894188b94eb75be0b156912ab8680b94e2579a96d9d71bff74b1c813b8592de6926fba8e5a030a88d8b4b208 - -### Client Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-darwin-amd64.tar.gz) | 22c4d1031297ea1833b3cd3e6805008c34b66f932ead3818db3eb2663a71510a8cdb53a05852991d54e354800ee97a2aad4afc31726d956f38c674929ce10778 -[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-darwin-arm64.tar.gz) | 6be320d2075d8a7835751c019556059ff2fca704d0bbeeff181248492d8ed6fcc2d6d6b68c509e4453431100b06a20268e61b9e434b638a78ebfad68e7c41276 -[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-386.tar.gz) | e63ac6b7127591068626a3d7caf0e1bae6390106f6c93efae34b18e38af257f1521635eb2adf76c40ad0f0d9a5397947bbb0215087d4d2e87ce6f253b6aec1a4 -[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-amd64.tar.gz) | 12dc8dc4997b71038c377bfd9869610110cebb20afcb051e85c86832f75bc8e7eabbb08b5caa00423c5f8df68210ad5ca140a61d4a8e9ad8640f648250205752 -[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-arm.tar.gz) | 0a7f8df6abfe9971f778add6771135d7079c245b18dd941eacf1230f75f461e7d8302142584aa4d60062c8cfd4e021f21ae5aa428d82b5fbe3697bda0e5854ff -[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-arm64.tar.gz) | b1442640ac1e45268e9916d0c51e711b7640fd2594ecad05a0d990c19db2e0dcde53cc90fb13588a2b926e25c831f62bf5461fa9c8e6a03a83573cc1c3791903 -[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-ppc64le.tar.gz) | e5a028da7fcb24aee85d010741c864fa4e5a3d6c87223b5c397686107a53dd2801a8c75cf9e1046ab28c97b06a5457aa6b3e4f809cd46cbe4858f78b2cb6a4df -[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-linux-s390x.tar.gz) | 4d3fce13d8f29e801c4d7355f83ded4d2e4abcc0b788f09d616ef7f89bd04e9d92d0b32e6e365118e618b32020d8b43e4cbd59a82262cc787b98f42e7df4ddbc -[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-windows-386.tar.gz) | 3bbe15f8856cab69c727b02766024e1bb430add8ad18216929a96d7731d255c5d5bb6b678a4d4e7a021f2e976633b69c0516c2260dcc0bee7d2447f64bd52fe8 -[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-windows-amd64.tar.gz) | 1833d8b09d5524df91120115667f897df47ad66edb57d2570e022234794c4d0d09212fca9b0b64e21ccc8ce6dcd41080bf9198c81583949cb8001c749f25e8a0 -[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-client-windows-arm64.tar.gz) | c0819674e11923b38d2df7cb9955929247a5b0752c93fc5215300da3514c592348cbe649a5c6fd6ac63500c6d68cf61a2733c099788164547e3f7738afe78ecf - -### Server Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-server-linux-amd64.tar.gz) | acd0b0b6723789780fd536894a965001056e94e92e2070edacdb53d2d879f56a90cc2c1ad0ff6d634ed74ef4debcefa01eee9f675cc4c70063da6cc52cc140d3 -[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-server-linux-arm64.tar.gz) | 31321659424b4847ec456ae507486efe57c8e903c2bc450df65ffc3bc90011ba050e8351ab32133943dfebd9d6e8ad47f2546a7cdc47e424cdaf0dc7247e08c3 -[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-server-linux-ppc64le.tar.gz) | fe81aa313be46ed5cc91507e58bc165e98722921d33473c29d382dceb948b1ffc0437d74825277a7da487f9390dec64f6a70617b05e0441c106fa87af737b90c -[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-server-linux-s390x.tar.gz) | 69a54f40e7a8684a6a1606f0463266d83af615f70a55d750031d82601c8070f4f9161048018c78e0859faa631ec9984fc20af3bc17240c8fc9394c6cbffacaf9 - -### Node Binaries - -filename | sha512 hash --------- | ----------- -[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-node-linux-amd64.tar.gz) | 797a5df349e571330e8090bd78f024d659d0d46e8a7352210b80ac594ef50dc2f3866240b75f7c0d2e08fa526388d0dfdcb91b4686f01b547c860a2d0a9846a7 -[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-node-linux-arm64.tar.gz) | 552a114facbd42c655574953186ba15a91c061b3db9ad25e665892c355347bf841e1bf716f8e28a16f1f1b37492911103212ec452bf5e663f8fcf26fae3ccc6a -[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-node-linux-ppc64le.tar.gz) | 7f08bad1921127fdceba7deb58d305e0b599de7ab588da936ff753ab4c6410b5db0634d71094e97ee1baeaccc491370c88268f6a540eedb556c90fb1ce350eda -[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-node-linux-s390x.tar.gz) | 4d1ac168b4591bf5ed7773d87eb47e64eb322adb6fd22b89f4f79c9849aee70188f0fa04a18775feff6f9baf95277499c56cd471a56240a87f9810c82434ba35 -[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.34.0-alpha.1/kubernetes-node-windows-amd64.tar.gz) | 896e508aa1c0bb3249c01554aea0ea25d65c4d9740772f8c053ded411b89a34a1c1e954e62fad10a1366cb0a9534af9b3d4e0a46acd956b47eb801e900dfcbe6 - -### 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.0-alpha.1](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.0-alpha.1](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.0-alpha.1](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.0-alpha.1](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.0-alpha.1](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.0-alpha.1](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.33.0 - -## Urgent Upgrade Notes - -### (No, really, you MUST read this before you upgrade) - - - For metrics `apiserver_cache_list_fetched_objects_total`, `apiserver_cache_list_returned_objects_total`, `apiserver_cache_list_total` replace `resource_prefix` label with API `group` and `resource` labels. - For metrics `etcd_request_duration_seconds`, `etcd_requests_total` and `etcd_request_errors_total` replace `type` label with API `resource` and `group` label. - For metric `apiserver_selfrequest_total` add a API `group` label. - For metrics `apiserver_watch_events_sizes` and `apiserver_watch_events_total` replace API `kind` label with `resource` label. - For metrics `apiserver_request_body_size_bytes`, `apiserver_storage_events_received_total`, `apiserver_storage_list_evaluated_objects_total`, `apiserver_storage_list_fetched_objects_total`, `apiserver_storage_list_returned_objects_total`, `apiserver_storage_list_total`, `apiserver_watch_cache_events_dispatched_total`, `apiserver_watch_cache_events_received_total`, `apiserver_watch_cache_initializations_total`, `apiserver_watch_cache_resource_version`, `watch_cache_capacity`, `apiserver_init_events_total`, `apiserver_terminated_watchers_total`, `watch_cache_capacity_increase_total`, `watch_cache_capacity_decrease_total`, `apiserver_watch_cache_read_wait_seconds`, `apiserver_watch_cache_consistent_read_total`, `apiserver_storage_consistency_checks_total`, `etcd_bookmark_counts`, `storage_decode_errors_total` extract the API group from `resource` label and put it in new `group` label. ([#131845](https://github.com/kubernetes/kubernetes/pull/131845), [@serathius](https://github.com/serathius)) [SIG API Machinery, Etcd, Instrumentation and Testing] - - Kubelet: removed the deprecated flag `--cloud-config` from the command line. ([#130161](https://github.com/kubernetes/kubernetes/pull/130161), [@carlory](https://github.com/carlory)) [SIG Cloud Provider, Node and Scalability] - - Scheduling Framework exposes NodeInfos to the PreFilterPlugins. - The PreFilterPlugins need to accept the NodeInfo list from the arguments. ([#130720](https://github.com/kubernetes/kubernetes/pull/130720), [@saintube](https://github.com/saintube)) [SIG Node, Scheduling, Storage and Testing] - -## Changes by Kind - -### Deprecation - -- Deprecate preferences field in kubeconfig in favor of kuberc ([#131741](https://github.com/kubernetes/kubernetes/pull/131741), [@soltysh](https://github.com/soltysh)) [SIG API Machinery, CLI, Cluster Lifecycle and Testing] -- Kubeadm: consistently print an 'error: ' prefix before errors. ([#132080](https://github.com/kubernetes/kubernetes/pull/132080), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] -- Kubeadm: only expose non-deprecated klog flags, 'v' and 'vmodule', to align with KEP https://features.k8s.io/2845 ([#131647](https://github.com/kubernetes/kubernetes/pull/131647), [@carsontham](https://github.com/carsontham)) [SIG Cluster Lifecycle] -- [cloud-provider] respect the "exclude-from-external-load-balancers=false" label ([#131085](https://github.com/kubernetes/kubernetes/pull/131085), [@kayrus](https://github.com/kayrus)) [SIG Cloud Provider and Network] - -### API Change - -- #### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.: - - ([#131996](https://github.com/kubernetes/kubernetes/pull/131996), [@ritazh](https://github.com/ritazh)) [SIG Node and Testing] -- DRA API: resource.k8s.io/v1alpha3 now only contains DeviceTaintRule. All other types got removed because they became obsolete when introducing the v1beta1 API in 1.32. - before updating a cluster where resourceclaims, resourceclaimtemplates, deviceclasses, or resourceslices might have been stored using Kubernetes < 1.32, delete all of those resources before updating and recreate them as needed while running Kubernetes >= 1.32. ([#132000](https://github.com/kubernetes/kubernetes/pull/132000), [@pohly](https://github.com/pohly)) [SIG Etcd, Node, Scheduling and Testing] -- Extends the nodeports scheduling plugin to consider hostPorts used by restartable init containers. ([#132040](https://github.com/kubernetes/kubernetes/pull/132040), [@avrittrohwer](https://github.com/avrittrohwer)) [SIG Scheduling and Testing] -- Kube-apiserver: Caching of authorization webhook decisions for authorized and unauthorized requests can now be disabled in the `--authorization-config` file by setting the new fields `cacheAuthorizedRequests` or `cacheUnauthorizedRequests` to `false` explicitly. See https://kubernetes.io/docs/reference/access-authn-authz/authorization/#using-configuration-file-for-authorization for more details. ([#129237](https://github.com/kubernetes/kubernetes/pull/129237), [@rfranzke](https://github.com/rfranzke)) [SIG API Machinery and Auth] -- Kube-apiserver: Promoted the `StructuredAuthenticationConfiguration` feature gate to GA. ([#131916](https://github.com/kubernetes/kubernetes/pull/131916), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-apiserver: the AuthenticationConfiguration type accepted in `--authentication-config` files has been promoted to `apiserver.config.k8s.io/v1`. ([#131752](https://github.com/kubernetes/kubernetes/pull/131752), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-log-runner: rotating log output into a new file when reaching a certain file size can be requested via the new `-log-file-size` parameter. `-log-file-age` enables automatical removal of old output files. Periodic flushing can be requested through ` -flush-interval`. ([#127667](https://github.com/kubernetes/kubernetes/pull/127667), [@zylxjtu](https://github.com/zylxjtu)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Release, Scheduling, Storage, Testing and Windows] -- Kubectl: graduated `kuberc` support to beta. A `kuberc` configuration file provides a mechanism for customizing kubectl behavior (separate from kubeconfig, which configured cluster access across different clients). ([#131818](https://github.com/kubernetes/kubernetes/pull/131818), [@soltysh](https://github.com/soltysh)) [SIG CLI and Testing] -- Promote the RelaxedEnvironmentVariableValidation feature gate to GA and lock it in the default enabled state. ([#132054](https://github.com/kubernetes/kubernetes/pull/132054), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Apps, Architecture, Node and Testing] -- Remove inaccurate statement about requiring ports from pod spec hostNetwork field ([#130994](https://github.com/kubernetes/kubernetes/pull/130994), [@BenTheElder](https://github.com/BenTheElder)) [SIG Network and Node] -- TBD ([#131318](https://github.com/kubernetes/kubernetes/pull/131318), [@aojea](https://github.com/aojea)) [SIG API Machinery, Apps, Architecture, Auth, Etcd, Network and Testing] -- The validation of `replicas` field in the ReplicationController `/scale` subresource has been migrated to declarative validation. - If the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. - If the `DeclarativeValidationTakeover` feature gate is enabled, declarative validation is the primary source of errors for migrated fields. ([#131664](https://github.com/kubernetes/kubernetes/pull/131664), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery and Apps] -- The validation-gen code generator generates validation code that supports validation ratcheting. ([#132236](https://github.com/kubernetes/kubernetes/pull/132236), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps, Auth and Node] -- Update etcd version to v3.6.0 ([#131501](https://github.com/kubernetes/kubernetes/pull/131501), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] -- When the IsDNS1123SubdomainWithUnderscore function returns an error, it will return the correct regex information dns1123SubdomainFmtWithUnderscore. ([#132034](https://github.com/kubernetes/kubernetes/pull/132034), [@ChosenFoam](https://github.com/ChosenFoam)) [SIG Network] -- Zero-value `metadata.creationTimestamp` values are now omitted and no longer serialize an explicit `null` in JSON, YAML, and CBOR output ([#130989](https://github.com/kubernetes/kubernetes/pull/130989), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Scheduling, Storage and Testing] - -### Feature - -- Add a flag to `kubectl version` that detects whether a client/server version mismatch is outside the officially supported range. ([#127365](https://github.com/kubernetes/kubernetes/pull/127365), [@omerap12](https://github.com/omerap12)) [SIG CLI] -- Add support for CEL expressions with escaped names in structured authentication config. Using `[` for accessing claims or user data is preferred when names contain characters that would need to be escaped. CEL optionals via `?` can be used in places where `has` cannot be used, i.e. `claims[?"kubernetes.io"]` or `user.extra[?"domain.io/foo"]`. ([#131574](https://github.com/kubernetes/kubernetes/pull/131574), [@enj](https://github.com/enj)) [SIG API Machinery and Auth] -- Added Traffic Distribution field to `kubectl describe service` output ([#131491](https://github.com/kubernetes/kubernetes/pull/131491), [@tchap](https://github.com/tchap)) [SIG CLI] -- Added a `--show-swap` option to `kubectl top` subcommands ([#129458](https://github.com/kubernetes/kubernetes/pull/129458), [@iholder101](https://github.com/iholder101)) [SIG CLI] -- Added alpha metrics for compatibility versioning ([#131842](https://github.com/kubernetes/kubernetes/pull/131842), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Architecture, Instrumentation and Scheduling] -- Enabling completion for aliases defined in kuberc ([#131586](https://github.com/kubernetes/kubernetes/pull/131586), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] -- Graduate ResilientWatchCacheInitialization to GA ([#131979](https://github.com/kubernetes/kubernetes/pull/131979), [@serathius](https://github.com/serathius)) [SIG API Machinery] -- Graduate configurable endpoints for anonymous authentication using the authentication configuration file to stable. ([#131654](https://github.com/kubernetes/kubernetes/pull/131654), [@vinayakankugoyal](https://github.com/vinayakankugoyal)) [SIG API Machinery and Testing] -- Graduated relaxed DNS search string validation to GA. For the Pod API, `.spec.dnsConfig.searches` - now allows an underscore (`_`) where a dash (`-`) would be allowed, and it allows search strings be a single dot `.`. ([#132036](https://github.com/kubernetes/kubernetes/pull/132036), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Network and Testing] -- Graduated scheduler `QueueingHint` support to GA (general availability) ([#131973](https://github.com/kubernetes/kubernetes/pull/131973), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Kube-apiserver: Promoted `ExternalServiceAccountTokenSigner` feature to beta, which enables external signing of service account tokens and fetching of public verifying keys, by enabling the beta `ExternalServiceAccountTokenSigner` feature gate and specifying `--service-account-signing-endpoint`. The flag value can either be the location of a Unix domain socket on a filesystem, or be prefixed with an @ symbol and name a Unix domain socket in the abstract socket namespace. ([#131300](https://github.com/kubernetes/kubernetes/pull/131300), [@HarshalNeelkamal](https://github.com/HarshalNeelkamal)) [SIG API Machinery, Auth and Testing] -- Kube-controller-manager events to support contextual logging. ([#128351](https://github.com/kubernetes/kubernetes/pull/128351), [@mengjiao-liu](https://github.com/mengjiao-liu)) [SIG API Machinery] -- Kube-proxy: Check if IPv6 is available on Linux before using it ([#131265](https://github.com/kubernetes/kubernetes/pull/131265), [@rikatz](https://github.com/rikatz)) [SIG Network] -- Kubeadm: add support for ECDSA-P384 as an encryption algorithm type in v1beta4. ([#131677](https://github.com/kubernetes/kubernetes/pull/131677), [@lalitc375](https://github.com/lalitc375)) [SIG Cluster Lifecycle] -- Kubeadm: fixed issue where etcd member promotion fails with an error saying the member was already promoted ([#130782](https://github.com/kubernetes/kubernetes/pull/130782), [@BernardMC](https://github.com/BernardMC)) [SIG Cluster Lifecycle] -- Kubeadm: graduated the `NodeLocalCRISocket` feature gate to beta and enabed it by default. When its enabled, kubeadm will: - 1. Generate a `/var/lib/kubelet/instance-config.yaml` file to customize the `containerRuntimeEndpoint` field in per-node kubelet configurations. - 2. Remove the `kubeadm.alpha.kubernetes.io/cri-socket` annotation from nodes during upgrade operations. - 3. Remove the `--container-runtime-endpoint` flag from the `/var/lib/kubelet/kubeadm-flags.env` file during upgrades. ([#131981](https://github.com/kubernetes/kubernetes/pull/131981), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Cluster Lifecycle] -- Kubeadm: switched the validation check for Linux kernel version to throw warnings instead of errors. ([#131919](https://github.com/kubernetes/kubernetes/pull/131919), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle and Node] -- Kubelet: the `--image-credential-provider-config` flag previously only accepted an individual file, but can now specify a directory path as well; when a directory is specified, all .json/.yaml/.yml files in the directory are loaded and merged in lexicographical order. ([#131658](https://github.com/kubernetes/kubernetes/pull/131658), [@dims](https://github.com/dims)) [SIG Auth and Node] -- Kubernetes api-server now merges selectors built from matchLabelKeys into the labelSelector of topologySpreadConstraints, - aligning Pod Topology Spread with the approach used by Inter-Pod Affinity. - - To avoid breaking existing pods that use matchLabelKeys, the current scheduler behavior will be preserved until it is removed in v1.34. - Therefore, do not upgrade your scheduler directly from v1.32 to v1.34. - Instead, upgrade step-by-step (from v1.32 to v1.33, then to v1.34), - ensuring that any pods created at v1.32 with matchLabelKeys are either removed or already scheduled by the time you reach v1.34. - - If you maintain controllers that previously relied on matchLabelKeys (for instance, to simulate scheduling), - you likely no longer need to handle matchLabelKeys directly. Instead, you can just rely on the labelSelector field going forward. - - Additionally, a new feature gate `MatchLabelKeysInPodTopologySpreadSelectorMerge`, which is enabled by default, has been - added to control this behavior. ([#129874](https://github.com/kubernetes/kubernetes/pull/129874), [@mochizuki875](https://github.com/mochizuki875)) [SIG Apps, Node, Scheduling and Testing] -- Kubernetes is now built using Go 1.24.3 ([#131934](https://github.com/kubernetes/kubernetes/pull/131934), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- Kubernetes is now built using Go 1.24.4 ([#132222](https://github.com/kubernetes/kubernetes/pull/132222), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] -- LeaseLocks can now have custom Labels that different holders will overwrite when they become the holder of the underlying lease. ([#131632](https://github.com/kubernetes/kubernetes/pull/131632), [@DerekFrank](https://github.com/DerekFrank)) [SIG API Machinery] -- Non-scheduling related errors (e.g., network errors) don't lengthen the Pod scheduling backoff time. ([#128748](https://github.com/kubernetes/kubernetes/pull/128748), [@sanposhiho](https://github.com/sanposhiho)) [SIG Scheduling and Testing] -- Promote feature OrderedNamespaceDeletion to GA. ([#131514](https://github.com/kubernetes/kubernetes/pull/131514), [@cici37](https://github.com/cici37)) [SIG API Machinery and Testing] -- Removed "endpoint-controller" and "workload-leader-election" FlowSchemas from the default APF configuration. - - migrate the lock type used in the leader election in your workloads from configmapsleases/endpointsleases to leases. ([#131215](https://github.com/kubernetes/kubernetes/pull/131215), [@tosi3k](https://github.com/tosi3k)) [SIG API Machinery, Apps, Network, Scalability and Scheduling] -- The PreferSameTrafficDistribution feature gate is now enabled by default, - enabling the `PreferSameNode` traffic distribution value for Services. ([#132127](https://github.com/kubernetes/kubernetes/pull/132127), [@danwinship](https://github.com/danwinship)) [SIG Apps and Network] -- Updated the built in `system:monitoring` role with permission to access kubelet metrics endpoints. ([#132178](https://github.com/kubernetes/kubernetes/pull/132178), [@gavinkflam](https://github.com/gavinkflam)) [SIG Auth] - -### Failing Test - -- Kube-apiserver: The --service-account-signing-endpoint flag now only validates the format of abstract socket names ([#131509](https://github.com/kubernetes/kubernetes/pull/131509), [@liggitt](https://github.com/liggitt)) [SIG API Machinery and Auth] - -### Bug or Regression - -- Check for newer resize fields when deciding recovery feature's status in kubelet ([#131418](https://github.com/kubernetes/kubernetes/pull/131418), [@gnufied](https://github.com/gnufied)) [SIG Storage] -- DRA: ResourceClaims requesting a fixed number of devices with `adminAccess` will no longer be allocated the same device multiple times. ([#131299](https://github.com/kubernetes/kubernetes/pull/131299), [@nojnhuh](https://github.com/nojnhuh)) [SIG Node] -- Disable reading of disk geometry before calling expansion for ext and xfs filesystems ([#131568](https://github.com/kubernetes/kubernetes/pull/131568), [@gnufied](https://github.com/gnufied)) [SIG Storage] -- Do not expand PVCs annotated with node-expand-not-required ([#131907](https://github.com/kubernetes/kubernetes/pull/131907), [@gnufied](https://github.com/gnufied)) [SIG API Machinery, Etcd, Node, Storage and Testing] -- Do not expand volume on the node, if controller expansion is finished ([#131868](https://github.com/kubernetes/kubernetes/pull/131868), [@gnufied](https://github.com/gnufied)) [SIG Storage] -- Do not log error event when waiting for expansion on the kubelet ([#131408](https://github.com/kubernetes/kubernetes/pull/131408), [@gnufied](https://github.com/gnufied)) [SIG Storage] -- Do not remove CSI json file if volume is already mounted on subsequent errors ([#131311](https://github.com/kubernetes/kubernetes/pull/131311), [@gnufied](https://github.com/gnufied)) [SIG Storage] -- Fix ReplicationController reconciliation when the DeploymentReplicaSetTerminatingReplicas feature gate is enabled ([#131822](https://github.com/kubernetes/kubernetes/pull/131822), [@atiratree](https://github.com/atiratree)) [SIG Apps] -- Fix a bug causing unexpected delay of creating pods for newly created jobs ([#132109](https://github.com/kubernetes/kubernetes/pull/132109), [@linxiulei](https://github.com/linxiulei)) [SIG Apps and Testing] -- Fix a bug in Job controller which could result in creating unnecessary Pods for a Job which is already - recognized as finished (successful or failed). ([#130333](https://github.com/kubernetes/kubernetes/pull/130333), [@kmala](https://github.com/kmala)) [SIG Apps and Testing] -- Fix the allocatedResourceStatuses Field name mismatch in PVC status validation ([#131213](https://github.com/kubernetes/kubernetes/pull/131213), [@carlory](https://github.com/carlory)) [SIG Apps] -- Fixed a bug in CEL's common.UnstructuredToVal where `==` evaluates to false for identical objects when a field is present but the value is null. This bug does not impact the Kubernetes API. ([#131559](https://github.com/kubernetes/kubernetes/pull/131559), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery] -- Fixed a bug that caused duplicate validation when updating a ReplicaSet. ([#131873](https://github.com/kubernetes/kubernetes/pull/131873), [@gavinkflam](https://github.com/gavinkflam)) [SIG Apps] -- Fixed a panic issue related to kubectl revision history kubernetes/kubectl#1724 ([#130503](https://github.com/kubernetes/kubernetes/pull/130503), [@tahacodes](https://github.com/tahacodes)) [SIG CLI] -- Fixed a possible deadlock in the watch client that could happen if the watch was not stopped. ([#131266](https://github.com/kubernetes/kubernetes/pull/131266), [@karlkfi](https://github.com/karlkfi)) [SIG API Machinery] -- Fixed an incorrect reference to `JoinConfigurationKind` in the error message when no ResetConfiguration is found during `kubeadm reset` with the `--config` flag. ([#132258](https://github.com/kubernetes/kubernetes/pull/132258), [@J3m3](https://github.com/J3m3)) [SIG Cluster Lifecycle] -- Fixed an issue where `insufficientResources` was logged as a pointer during pod preemption, making logs more readable. ([#132183](https://github.com/kubernetes/kubernetes/pull/132183), [@chrisy-x](https://github.com/chrisy-x)) [SIG Node] -- Fixed incorrect behavior for AllocationMode: All in ResourceClaim when used in subrequests. ([#131660](https://github.com/kubernetes/kubernetes/pull/131660), [@mortent](https://github.com/mortent)) [SIG Node] -- Fixed misleading response codes in admission control metrics. ([#132165](https://github.com/kubernetes/kubernetes/pull/132165), [@gavinkflam](https://github.com/gavinkflam)) [SIG API Machinery, Architecture and Instrumentation] -- Fixes an issue where Windows kube-proxy's ModifyLoadBalancer API updates did not match HNS state in version 15.4. ModifyLoadBalancer policy is supported from Kubernetes 1.31+. ([#131506](https://github.com/kubernetes/kubernetes/pull/131506), [@princepereira](https://github.com/princepereira)) [SIG Windows] -- HPA controller will no longer emit a 'FailedRescale' event if a scale operation initially fails due to a conflict but succeeds after a retry; a 'SuccessfulRescale' event will be emitted instead. A 'FailedRescale' event is still emitted if retries are exhausted. ([#132007](https://github.com/kubernetes/kubernetes/pull/132007), [@AumPatel1](https://github.com/AumPatel1)) [SIG Apps and Autoscaling] -- Improve error message when a pod with user namespaces is created and the runtime doesn't support user namespaces. ([#131623](https://github.com/kubernetes/kubernetes/pull/131623), [@rata](https://github.com/rata)) [SIG Node] -- Kube-apiserver: Fixes OIDC discovery document publishing when external service account token signing is enabled ([#131493](https://github.com/kubernetes/kubernetes/pull/131493), [@hoskeri](https://github.com/hoskeri)) [SIG API Machinery, Auth and Testing] -- Kube-apiserver: cronjob objects now default empty `spec.jobTemplate.spec.podFailurePolicy.rules[*].onPodConditions[*].status` fields as documented, avoiding validation failures during write requests. ([#131525](https://github.com/kubernetes/kubernetes/pull/131525), [@carlory](https://github.com/carlory)) [SIG Apps] -- Kube-proxy: Remove iptables cli wait interval flag ([#131961](https://github.com/kubernetes/kubernetes/pull/131961), [@cyclinder](https://github.com/cyclinder)) [SIG Network] -- Kube-scheduler: in Kubernetes 1.33, the number of devices that can be allocated per ResourceClaim was accidentally reduced to 16. Now the supported number of devices per ResourceClaim is 32 again. ([#131662](https://github.com/kubernetes/kubernetes/pull/131662), [@mortent](https://github.com/mortent)) [SIG Node] -- Kubelet: close a loophole where static pods could reference arbitrary ResourceClaims. The pods created by the kubelet then don't run due to a sanity check, but such references shouldn't be allowed regardless. ([#131844](https://github.com/kubernetes/kubernetes/pull/131844), [@pohly](https://github.com/pohly)) [SIG Apps, Auth and Node] -- Kubelet: fix a bug where the unexpected NodeResizeError condition was in PVC status when the csi driver does not support node volume expansion and the pvc has the ReadWriteMany access mode. ([#131495](https://github.com/kubernetes/kubernetes/pull/131495), [@carlory](https://github.com/carlory)) [SIG Storage] -- Reduce 5s delay of tainting `node.kubernetes.io/unreachable:NoExecute` when a Node becomes unreachable ([#120816](https://github.com/kubernetes/kubernetes/pull/120816), [@tnqn](https://github.com/tnqn)) [SIG Apps and Node] -- Skip pod backoff completely when PodMaxBackoffDuration kube-scheduler option is set to zero and SchedulerPopFromBackoffQ feature gate is enabled. ([#131965](https://github.com/kubernetes/kubernetes/pull/131965), [@macsko](https://github.com/macsko)) [SIG Scheduling] -- The shorthand for --output flag in kubectl explain was accidentally deleted, but has been added back. ([#131962](https://github.com/kubernetes/kubernetes/pull/131962), [@superbrothers](https://github.com/superbrothers)) [SIG CLI] -- `kubectl create|delete|get|replace --raw` commands now honor server root paths specified in the kubeconfig file. ([#131165](https://github.com/kubernetes/kubernetes/pull/131165), [@liggitt](https://github.com/liggitt)) [SIG API Machinery] - -### Other (Cleanup or Flake) - -- Added a warning to `kubectl attach`, notifying / reminding users that commands and output are available via the `log` subresource of that Pod. ([#127183](https://github.com/kubernetes/kubernetes/pull/127183), [@mochizuki875](https://github.com/mochizuki875)) [SIG Auth, CLI, Node and Security] -- Bump cel-go dependency to v0.25.0. The changeset is available at: https://github.com/google/cel-go/compare/v0.23.2...v0.25.0 ([#131444](https://github.com/kubernetes/kubernetes/pull/131444), [@erdii](https://github.com/erdii)) [SIG API Machinery, Auth, Cloud Provider and Node] -- Bump kube dns to v1.26.4 ([#132012](https://github.com/kubernetes/kubernetes/pull/132012), [@pacoxu](https://github.com/pacoxu)) [SIG Cloud Provider] -- By default the binaries like kube-apiserver are built with "grpcnotrace" tag enabled. Please use DBG flag if you want to enable golang tracing. ([#132210](https://github.com/kubernetes/kubernetes/pull/132210), [@dims](https://github.com/dims)) [SIG Architecture] -- Changed apiserver to treat failures decoding a mutating webhook patch as failures to call the webhook so they trigger the webhook failurePolicy and count against metrics like `webhook_fail_open_count` ([#131627](https://github.com/kubernetes/kubernetes/pull/131627), [@dims](https://github.com/dims)) [SIG API Machinery] -- DRA kubelet: logging now uses `driverName` like the rest of the Kubernetes components, instead of `pluginName`. ([#132096](https://github.com/kubernetes/kubernetes/pull/132096), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- DRA kubelet: recovery from mistakes like scheduling a pod onto a node with the required driver not running is a bit simpler now because the kubelet does not block pod deletion unnecessarily. ([#131968](https://github.com/kubernetes/kubernetes/pull/131968), [@pohly](https://github.com/pohly)) [SIG Node and Testing] -- Fixed some missing white spaces in the flag descriptions and logs. ([#131562](https://github.com/kubernetes/kubernetes/pull/131562), [@logica0419](https://github.com/logica0419)) [SIG Network] -- Hack/update-codegen.sh now automatically ensures goimports and protoc ([#131459](https://github.com/kubernetes/kubernetes/pull/131459), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery] -- Kube-apiserver: removed the deprecated `apiserver_encryption_config_controller_automatic_reload_success_total` and `apiserver_encryption_config_controller_automatic_reload_failure_total` metrics in favor of `apiserver_encryption_config_controller_automatic_reloads_total`. ([#132238](https://github.com/kubernetes/kubernetes/pull/132238), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] -- Kube-scheduler: removed the deprecated scheduler_scheduler_cache_size metric in favor of scheduler_cache_size ([#131425](https://github.com/kubernetes/kubernetes/pull/131425), [@carlory](https://github.com/carlory)) [SIG Scheduling] -- Kubeadm: fixed missing space when printing the warning about pause image mismatch. ([#131563](https://github.com/kubernetes/kubernetes/pull/131563), [@logica0419](https://github.com/logica0419)) [SIG Cluster Lifecycle] -- Kubeadm: made the coredns deployment manifest use named ports consistently for the liveness and readiness probes. ([#131587](https://github.com/kubernetes/kubernetes/pull/131587), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] -- Kubectl interactive delete: treat empty newline input as N ([#132251](https://github.com/kubernetes/kubernetes/pull/132251), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] -- Migrate pkg/kubelet/status to contextual logging ([#130852](https://github.com/kubernetes/kubernetes/pull/130852), [@Chulong-Li](https://github.com/Chulong-Li)) [SIG Node] -- Promote `apiserver_authentication_config_controller_automatic_reloads_total` and `apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds` metrics to BETA. ([#131798](https://github.com/kubernetes/kubernetes/pull/131798), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Instrumentation] -- Promote `apiserver_authorization_config_controller_automatic_reloads_total` and `apiserver_authorization_config_controller_automatic_reload_last_timestamp_seconds` metrics to BETA. ([#131768](https://github.com/kubernetes/kubernetes/pull/131768), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Instrumentation] -- Promoted the `SeparateTaintEvictionController` feature gate to GA; it is now enabled unconditionally. ([#122634](https://github.com/kubernetes/kubernetes/pull/122634), [@carlory](https://github.com/carlory)) [SIG API Machinery, Apps, Node and Testing] -- Removed generally available feature-gate `PodDisruptionConditions`. ([#129501](https://github.com/kubernetes/kubernetes/pull/129501), [@carlory](https://github.com/carlory)) [SIG Apps] -- Removes support for API streaming from the `List() method` of the dynamic client. ([#132229](https://github.com/kubernetes/kubernetes/pull/132229), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery, CLI and Testing] -- Removes support for API streaming from the `List() method` of the metadata client. ([#132149](https://github.com/kubernetes/kubernetes/pull/132149), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- Removes support for API streaming from the `List() method` of the typed client. ([#132257](https://github.com/kubernetes/kubernetes/pull/132257), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery and Testing] -- Removes support for API streaming from the rest client. ([#132285](https://github.com/kubernetes/kubernetes/pull/132285), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery] -- Types: CycleState, StateData, StateKey and ErrNotFound moved from pkg/scheduler/framework to k8s.io/kube-scheduler/framework. - Type CycleState that is passed to each plugin in scheduler framework is changed to the new interface CycleState (in k8s.io/kube-scheduler/framework) ([#131887](https://github.com/kubernetes/kubernetes/pull/131887), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Scheduling, Storage and Testing] -- Updated CNI plugins to v1.7.1 ([#131602](https://github.com/kubernetes/kubernetes/pull/131602), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Cloud Provider, Node and Testing] -- Updated cri-tools to v1.33.0. ([#131406](https://github.com/kubernetes/kubernetes/pull/131406), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider] -- Upgrade CoreDNS to v1.12.1 ([#131151](https://github.com/kubernetes/kubernetes/pull/131151), [@yashsingh74](https://github.com/yashsingh74)) [SIG Cloud Provider and Cluster Lifecycle] - -## Dependencies - -### Added -- buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go: 63bb56e -- github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp: [v1.26.0](https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/tree/detectors/gcp/v1.26.0) -- github.com/bufbuild/protovalidate-go: [v0.9.1](https://github.com/bufbuild/protovalidate-go/tree/v0.9.1) -- github.com/envoyproxy/go-control-plane/envoy: [v1.32.4](https://github.com/envoyproxy/go-control-plane/tree/envoy/v1.32.4) -- github.com/envoyproxy/go-control-plane/ratelimit: [v0.1.0](https://github.com/envoyproxy/go-control-plane/tree/ratelimit/v0.1.0) -- github.com/go-jose/go-jose/v4: [v4.0.4](https://github.com/go-jose/go-jose/tree/v4.0.4) -- github.com/golang-jwt/jwt/v5: [v5.2.2](https://github.com/golang-jwt/jwt/tree/v5.2.2) -- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus: [v1.0.1](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/providers/prometheus/v1.0.1) -- github.com/grpc-ecosystem/go-grpc-middleware/v2: [v2.3.0](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/v2.3.0) -- github.com/spiffe/go-spiffe/v2: [v2.5.0](https://github.com/spiffe/go-spiffe/tree/v2.5.0) -- github.com/zeebo/errs: [v1.4.0](https://github.com/zeebo/errs/tree/v1.4.0) -- go.etcd.io/raft/v3: v3.6.0 -- go.opentelemetry.io/contrib/detectors/gcp: v1.34.0 -- go.opentelemetry.io/otel/sdk/metric: v1.34.0 - -### Changed -- cel.dev/expr: v0.19.1 → v0.23.1 -- cloud.google.com/go/compute/metadata: v0.5.0 → v0.6.0 -- github.com/Microsoft/hnslib: [v0.0.8 → v0.1.1](https://github.com/Microsoft/hnslib/compare/v0.0.8...v0.1.1) -- github.com/cncf/xds/go: [b4127c9 → 2f00578](https://github.com/cncf/xds/compare/b4127c9...2f00578) -- github.com/coredns/corefile-migration: [v1.0.25 → v1.0.26](https://github.com/coredns/corefile-migration/compare/v1.0.25...v1.0.26) -- github.com/cpuguy83/go-md2man/v2: [v2.0.4 → v2.0.6](https://github.com/cpuguy83/go-md2man/compare/v2.0.4...v2.0.6) -- github.com/envoyproxy/go-control-plane: [v0.13.0 → v0.13.4](https://github.com/envoyproxy/go-control-plane/compare/v0.13.0...v0.13.4) -- github.com/envoyproxy/protoc-gen-validate: [v1.1.0 → v1.2.1](https://github.com/envoyproxy/protoc-gen-validate/compare/v1.1.0...v1.2.1) -- github.com/fsnotify/fsnotify: [v1.7.0 → v1.9.0](https://github.com/fsnotify/fsnotify/compare/v1.7.0...v1.9.0) -- github.com/fxamacker/cbor/v2: [v2.7.0 → v2.8.0](https://github.com/fxamacker/cbor/compare/v2.7.0...v2.8.0) -- github.com/golang/glog: [v1.2.2 → v1.2.4](https://github.com/golang/glog/compare/v1.2.2...v1.2.4) -- github.com/google/cel-go: [v0.23.2 → v0.25.0](https://github.com/google/cel-go/compare/v0.23.2...v0.25.0) -- github.com/grpc-ecosystem/grpc-gateway/v2: [v2.24.0 → v2.26.3](https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.24.0...v2.26.3) -- github.com/ishidawataru/sctp: [7ff4192 → ae8eb7f](https://github.com/ishidawataru/sctp/compare/7ff4192...ae8eb7f) -- github.com/jonboulle/clockwork: [v0.4.0 → v0.5.0](https://github.com/jonboulle/clockwork/compare/v0.4.0...v0.5.0) -- github.com/modern-go/reflect2: [v1.0.2 → 35a7c28](https://github.com/modern-go/reflect2/compare/v1.0.2...35a7c28) -- github.com/spf13/cobra: [v1.8.1 → v1.9.1](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1) -- github.com/spf13/pflag: [v1.0.5 → v1.0.6](https://github.com/spf13/pflag/compare/v1.0.5...v1.0.6) -- github.com/vishvananda/netlink: [62fb240 → v1.3.1](https://github.com/vishvananda/netlink/compare/62fb240...v1.3.1) -- github.com/vishvananda/netns: [v0.0.4 → v0.0.5](https://github.com/vishvananda/netns/compare/v0.0.4...v0.0.5) -- go.etcd.io/bbolt: v1.3.11 → v1.4.0 -- go.etcd.io/etcd/api/v3: v3.5.21 → v3.6.1 -- go.etcd.io/etcd/client/pkg/v3: v3.5.21 → v3.6.1 -- go.etcd.io/etcd/client/v3: v3.5.21 → v3.6.1 -- go.etcd.io/etcd/pkg/v3: v3.5.21 → v3.6.1 -- go.etcd.io/etcd/server/v3: v3.5.21 → v3.6.1 -- go.etcd.io/gofail: v0.1.0 → v0.2.0 -- go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful: v0.42.0 → v0.44.0 -- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc: v0.58.0 → v0.60.0 -- go.opentelemetry.io/contrib/propagators/b3: v1.17.0 → v1.19.0 -- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/exporters/otlp/otlptrace: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/metric: v1.33.0 → v1.35.0 -- go.opentelemetry.io/otel/sdk: v1.33.0 → v1.34.0 -- go.opentelemetry.io/otel/trace: v1.33.0 → v1.35.0 -- go.opentelemetry.io/otel: v1.33.0 → v1.35.0 -- go.opentelemetry.io/proto/otlp: v1.4.0 → v1.5.0 -- google.golang.org/genproto/googleapis/api: e6fa225 → a0af3ef -- google.golang.org/genproto/googleapis/rpc: e6fa225 → a0af3ef -- google.golang.org/grpc: v1.68.1 → v1.72.1 -- k8s.io/gengo/v2: 1244d31 → 85fd79d -- k8s.io/system-validators: v1.9.1 → v1.10.1 -- k8s.io/utils: 3ea5e8c → 4c0f3b2 -- sigs.k8s.io/structured-merge-diff/v4: v4.6.0 → v4.7.0 - -### Removed -- cloud.google.com/go/accessapproval: v1.7.4 -- cloud.google.com/go/accesscontextmanager: v1.8.4 -- cloud.google.com/go/aiplatform: v1.58.0 -- cloud.google.com/go/analytics: v0.22.0 -- cloud.google.com/go/apigateway: v1.6.4 -- cloud.google.com/go/apigeeconnect: v1.6.4 -- cloud.google.com/go/apigeeregistry: v0.8.2 -- cloud.google.com/go/appengine: v1.8.4 -- cloud.google.com/go/area120: v0.8.4 -- cloud.google.com/go/artifactregistry: v1.14.6 -- cloud.google.com/go/asset: v1.17.0 -- cloud.google.com/go/assuredworkloads: v1.11.4 -- cloud.google.com/go/automl: v1.13.4 -- cloud.google.com/go/baremetalsolution: v1.2.3 -- cloud.google.com/go/batch: v1.7.0 -- cloud.google.com/go/beyondcorp: v1.0.3 -- cloud.google.com/go/bigquery: v1.58.0 -- cloud.google.com/go/billing: v1.18.0 -- cloud.google.com/go/binaryauthorization: v1.8.0 -- cloud.google.com/go/certificatemanager: v1.7.4 -- cloud.google.com/go/channel: v1.17.4 -- cloud.google.com/go/cloudbuild: v1.15.0 -- cloud.google.com/go/clouddms: v1.7.3 -- cloud.google.com/go/cloudtasks: v1.12.4 -- cloud.google.com/go/compute: v1.23.3 -- cloud.google.com/go/contactcenterinsights: v1.12.1 -- cloud.google.com/go/container: v1.29.0 -- cloud.google.com/go/containeranalysis: v0.11.3 -- cloud.google.com/go/datacatalog: v1.19.2 -- cloud.google.com/go/dataflow: v0.9.4 -- cloud.google.com/go/dataform: v0.9.1 -- cloud.google.com/go/datafusion: v1.7.4 -- cloud.google.com/go/datalabeling: v0.8.4 -- cloud.google.com/go/dataplex: v1.14.0 -- cloud.google.com/go/dataproc/v2: v2.3.0 -- cloud.google.com/go/dataqna: v0.8.4 -- cloud.google.com/go/datastore: v1.15.0 -- cloud.google.com/go/datastream: v1.10.3 -- cloud.google.com/go/deploy: v1.17.0 -- cloud.google.com/go/dialogflow: v1.48.1 -- cloud.google.com/go/dlp: v1.11.1 -- cloud.google.com/go/documentai: v1.23.7 -- cloud.google.com/go/domains: v0.9.4 -- cloud.google.com/go/edgecontainer: v1.1.4 -- cloud.google.com/go/errorreporting: v0.3.0 -- cloud.google.com/go/essentialcontacts: v1.6.5 -- cloud.google.com/go/eventarc: v1.13.3 -- cloud.google.com/go/filestore: v1.8.0 -- cloud.google.com/go/firestore: v1.14.0 -- cloud.google.com/go/functions: v1.15.4 -- cloud.google.com/go/gkebackup: v1.3.4 -- cloud.google.com/go/gkeconnect: v0.8.4 -- cloud.google.com/go/gkehub: v0.14.4 -- cloud.google.com/go/gkemulticloud: v1.1.0 -- cloud.google.com/go/gsuiteaddons: v1.6.4 -- cloud.google.com/go/iam: v1.1.5 -- cloud.google.com/go/iap: v1.9.3 -- cloud.google.com/go/ids: v1.4.4 -- cloud.google.com/go/iot: v1.7.4 -- cloud.google.com/go/kms: v1.15.5 -- cloud.google.com/go/language: v1.12.2 -- cloud.google.com/go/lifesciences: v0.9.4 -- cloud.google.com/go/logging: v1.9.0 -- cloud.google.com/go/longrunning: v0.5.4 -- cloud.google.com/go/managedidentities: v1.6.4 -- cloud.google.com/go/maps: v1.6.3 -- cloud.google.com/go/mediatranslation: v0.8.4 -- cloud.google.com/go/memcache: v1.10.4 -- cloud.google.com/go/metastore: v1.13.3 -- cloud.google.com/go/monitoring: v1.17.0 -- cloud.google.com/go/networkconnectivity: v1.14.3 -- cloud.google.com/go/networkmanagement: v1.9.3 -- cloud.google.com/go/networksecurity: v0.9.4 -- cloud.google.com/go/notebooks: v1.11.2 -- cloud.google.com/go/optimization: v1.6.2 -- cloud.google.com/go/orchestration: v1.8.4 -- cloud.google.com/go/orgpolicy: v1.12.0 -- cloud.google.com/go/osconfig: v1.12.4 -- cloud.google.com/go/oslogin: v1.13.0 -- cloud.google.com/go/phishingprotection: v0.8.4 -- cloud.google.com/go/policytroubleshooter: v1.10.2 -- cloud.google.com/go/privatecatalog: v0.9.4 -- cloud.google.com/go/pubsub: v1.34.0 -- cloud.google.com/go/pubsublite: v1.8.1 -- cloud.google.com/go/recaptchaenterprise/v2: v2.9.0 -- cloud.google.com/go/recommendationengine: v0.8.4 -- cloud.google.com/go/recommender: v1.12.0 -- cloud.google.com/go/redis: v1.14.1 -- cloud.google.com/go/resourcemanager: v1.9.4 -- cloud.google.com/go/resourcesettings: v1.6.4 -- cloud.google.com/go/retail: v1.14.4 -- cloud.google.com/go/run: v1.3.3 -- cloud.google.com/go/scheduler: v1.10.5 -- cloud.google.com/go/secretmanager: v1.11.4 -- cloud.google.com/go/security: v1.15.4 -- cloud.google.com/go/securitycenter: v1.24.3 -- cloud.google.com/go/servicedirectory: v1.11.3 -- cloud.google.com/go/shell: v1.7.4 -- cloud.google.com/go/spanner: v1.55.0 -- cloud.google.com/go/speech: v1.21.0 -- cloud.google.com/go/storagetransfer: v1.10.3 -- cloud.google.com/go/talent: v1.6.5 -- cloud.google.com/go/texttospeech: v1.7.4 -- cloud.google.com/go/tpu: v1.6.4 -- cloud.google.com/go/trace: v1.10.4 -- cloud.google.com/go/translate: v1.10.0 -- cloud.google.com/go/video: v1.20.3 -- cloud.google.com/go/videointelligence: v1.11.4 -- cloud.google.com/go/vision/v2: v2.7.5 -- cloud.google.com/go/vmmigration: v1.7.4 -- cloud.google.com/go/vmwareengine: v1.0.3 -- cloud.google.com/go/vpcaccess: v1.7.4 -- cloud.google.com/go/webrisk: v1.9.4 -- cloud.google.com/go/websecurityscanner: v1.6.4 -- cloud.google.com/go/workflows: v1.12.3 -- cloud.google.com/go: v0.112.0 -- github.com/BurntSushi/toml: [v0.3.1](https://github.com/BurntSushi/toml/tree/v0.3.1) -- github.com/census-instrumentation/opencensus-proto: [v0.4.1](https://github.com/census-instrumentation/opencensus-proto/tree/v0.4.1) -- github.com/client9/misspell: [v0.3.4](https://github.com/client9/misspell/tree/v0.3.4) -- github.com/cncf/udpa/go: [269d4d4](https://github.com/cncf/udpa/tree/269d4d4) -- github.com/ghodss/yaml: [v1.0.0](https://github.com/ghodss/yaml/tree/v1.0.0) -- github.com/go-kit/kit: [v0.9.0](https://github.com/go-kit/kit/tree/v0.9.0) -- github.com/go-logfmt/logfmt: [v0.4.0](https://github.com/go-logfmt/logfmt/tree/v0.4.0) -- github.com/go-stack/stack: [v1.8.0](https://github.com/go-stack/stack/tree/v1.8.0) -- github.com/golang-jwt/jwt/v4: [v4.5.2](https://github.com/golang-jwt/jwt/tree/v4.5.2) -- github.com/golang/mock: [v1.1.1](https://github.com/golang/mock/tree/v1.1.1) -- github.com/grpc-ecosystem/grpc-gateway: [v1.16.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.16.0) -- github.com/konsorten/go-windows-terminal-sequences: [v1.0.1](https://github.com/konsorten/go-windows-terminal-sequences/tree/v1.0.1) -- github.com/kr/logfmt: [b84e30a](https://github.com/kr/logfmt/tree/b84e30a) -- github.com/opentracing/opentracing-go: [v1.1.0](https://github.com/opentracing/opentracing-go/tree/v1.1.0) -- go.etcd.io/etcd/client/v2: v2.305.21 -- go.etcd.io/etcd/raft/v3: v3.5.21 -- go.uber.org/atomic: v1.7.0 -- golang.org/x/lint: d0100b6 -- google.golang.org/appengine: v1.4.0 -- google.golang.org/genproto: ef43131 -- honnef.co/go/tools: ea95bdf \ No newline at end of file diff --git a/CHANGELOG/CHANGELOG-1.35.md b/CHANGELOG/CHANGELOG-1.35.md new file mode 100644 index 0000000000000..0716b0f3d60a3 --- /dev/null +++ b/CHANGELOG/CHANGELOG-1.35.md @@ -0,0 +1,1575 @@ + + +- [v1.35.0](#v1350) + - [Downloads for v1.35.0](#downloads-for-v1350) + - [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) + - [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) + - [Deprecation](#deprecation) + - [API Change](#api-change) + - [Feature](#feature) + - [Documentation](#documentation) + - [Bug or Regression](#bug-or-regression) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake) + - [Dependencies](#dependencies) + - [Added](#added) + - [Changed](#changed) + - [Removed](#removed) +- [v1.35.0-rc.1](#v1350-rc1) + - [Downloads for v1.35.0-rc.1](#downloads-for-v1350-rc1) + - [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.35.0-rc.0](#changelog-since-v1350-rc0) + - [Changes by Kind](#changes-by-kind-1) + - [Feature](#feature-1) + - [Bug or Regression](#bug-or-regression-1) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-1) + - [Dependencies](#dependencies-1) + - [Added](#added-1) + - [Changed](#changed-1) + - [Removed](#removed-1) +- [v1.35.0-rc.0](#v1350-rc0) + - [Downloads for v1.35.0-rc.0](#downloads-for-v1350-rc0) + - [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.35.0-beta.0](#changelog-since-v1350-beta0) + - [Changes by Kind](#changes-by-kind-2) + - [Feature](#feature-2) + - [Bug or Regression](#bug-or-regression-2) + - [Dependencies](#dependencies-2) + - [Added](#added-2) + - [Changed](#changed-2) + - [Removed](#removed-2) +- [v1.35.0-beta.0](#v1350-beta0) + - [Downloads for v1.35.0-beta.0](#downloads-for-v1350-beta0) + - [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.35.0-alpha.3](#changelog-since-v1350-alpha3) + - [Changes by Kind](#changes-by-kind-3) + - [API Change](#api-change-1) + - [Feature](#feature-3) + - [Bug or Regression](#bug-or-regression-3) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-2) + - [Dependencies](#dependencies-3) + - [Added](#added-3) + - [Changed](#changed-3) + - [Removed](#removed-3) +- [v1.35.0-alpha.3](#v1350-alpha3) + - [Downloads for v1.35.0-alpha.3](#downloads-for-v1350-alpha3) + - [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.35.0-alpha.2](#changelog-since-v1350-alpha2) + - [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-4) + - [API Change](#api-change-2) + - [Feature](#feature-4) + - [Bug or Regression](#bug-or-regression-4) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-3) + - [Dependencies](#dependencies-4) + - [Added](#added-4) + - [Changed](#changed-4) + - [Removed](#removed-4) +- [v1.35.0-alpha.2](#v1350-alpha2) + - [Downloads for v1.35.0-alpha.2](#downloads-for-v1350-alpha2) + - [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.35.0-alpha.1](#changelog-since-v1350-alpha1) + - [Changes by Kind](#changes-by-kind-5) + - [Deprecation](#deprecation-1) + - [API Change](#api-change-3) + - [Feature](#feature-5) + - [Documentation](#documentation-1) + - [Bug or Regression](#bug-or-regression-5) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-4) + - [Dependencies](#dependencies-5) + - [Added](#added-5) + - [Changed](#changed-5) + - [Removed](#removed-5) +- [v1.35.0-alpha.1](#v1350-alpha1) + - [Downloads for v1.35.0-alpha.1](#downloads-for-v1350-alpha1) + - [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](#changelog-since-v1340-1) + - [Changes by Kind](#changes-by-kind-6) + - [API Change](#api-change-4) + - [Feature](#feature-6) + - [Bug or Regression](#bug-or-regression-6) + - [Other (Cleanup or Flake)](#other-cleanup-or-flake-5) + - [Dependencies](#dependencies-6) + - [Added](#added-6) + - [Changed](#changed-6) + - [Removed](#removed-6) + + + +# v1.35.0 + +[Documentation](https://docs.k8s.io) + +## Downloads for v1.35.0 + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes.tar.gz) | `478ae8101675fa873a3ad84c81c91604e70bdb947e3379564907916c8a3a1d4a0b7d2077e1d2701f18f2509a6fce0997d93a441ef6d1a17a2e90fdffdd4c13ec` +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-src.tar.gz) | `dc9fc72736999bc40fdf28a7668c8e183effe135893c98f0773b0a50fe018c2f49156026c490f201def57645bf6172c81e07c1c6cb2d80bfb6b246c94fb4c5aa` + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-darwin-amd64.tar.gz) | `e7d510566442afd96dd3759764b573719469bb0ef00086d536bd7af0b8af29ddf150e6ece5ae95856daaaf7f2454f45755ac300648c692508e445aca7a8bd0de` +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-darwin-arm64.tar.gz) | `cd3b216a5418ef2eb00aeb74bf0ebae34c41aa16419bd5bbe5cbb5d394570a38f54c88294aaa5bd7c27ef28c4f1aee2b5658beb4cd025258b6bbd522e8d499bc` +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-386.tar.gz) | `50250aefecc03afe5a6b1be8dffbd58efb4814fed2aae299ac3bbd3b32a40b47697897bafcc36f31f226c5fd2b185cb970e64674aa9ee60412e122128487598d` +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-amd64.tar.gz) | `a1469924896411ab3365628b301d2bbacaf235908cea47308498c9c351a17462ab4154928ef6f91cee849ff52600e394f2abe70f5165371ccfe6638446699d2c` +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-arm.tar.gz) | `df921ad2702a8bc90b8797d97e5ddba5d7d077d18f3b9e53a4594a432f628f52842ee5e26f70c16a82b4decf7c72cba1d04c43163c85026f9b0610fbde63e183` +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-arm64.tar.gz) | `0b332e13c9bb52093f57c4f2ae4ab103bc7f51e4c5dad2859300e7ece09ef303a9345ed3aea4d050b287f52dd8ed8d7cf9185c9e40ea5cc900c8d34e63eec83d` +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-ppc64le.tar.gz) | `07789dc2ec7e8439774d88437f0b1ee35d6b60a8bd23055b93dcf1461de5ae69aba0e0e99a0202892f6c70217388646e1592b087f048bb57e5ab10b1b0dfa956` +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-linux-s390x.tar.gz) | `6563b8d452d29e7f155563294478e39dba7311dd086cf9fb0bc62c94a139b7f5d81a5716880d8072cd864948988e68f2dcd607a8ec79e339224ed5f4bcd48dc9` +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-windows-386.tar.gz) | `522f96799bdaacdd1d10ab4c3a58d8fd86e45e6326c3b6538cc079ca951c28916bd1c8c9bb1d98f6257be0ba1ed91e97614407fe11a1c4bbea2c2052ba0feca7` +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-windows-amd64.tar.gz) | `149145263071c8e1a4d73efe4d1c868286e7cea37629f1c076d2f2683e6b63fb3387d867f3283c9950a3b5b830f005019fa03874e4d53dfa9ad489aaaa9f535b` +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-client-windows-arm64.tar.gz) | `2cffd56e01eaf24ace819cf9f4ef94187185978c8fa1192fd9d47236824ccfe745fe649d38c4351a016e0406bcfd1944178cb93af67b5e69015c04ab2ca5bf7c` + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-server-linux-amd64.tar.gz) | `23af53c49de841a0d5c19d9525d820cecc9d55367c132296a5f381d051438bf06dcddff3d0236df8ba6011a6aa5d0ffc31960d277c7f53a0ad98e66d6f8d6a0a` +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-server-linux-arm64.tar.gz) | `fd245273c6ace20abc893f868d678c4a24c0dbe7d5340087f852d245e59329e66f79afce489dc1b396908d2f005b132eca8d15a7664508fe923627bb2eddee18` +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-server-linux-ppc64le.tar.gz) | `68c48db8537c0470d2245740b8cdf3225efafc48a96646e369137e35931bd43324caf1394ee4b31774b0f43d44e6a4eaa5976186248a114d0e0feb2cb8953edc` +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-server-linux-s390x.tar.gz) | `dd71c4b5ab213452d41059772de3b0db2c71fc6f958280694b2c1b20151bded5b6beb1b03a40dc683ce2d587e9a8bbf3bf486b3965064945803af4f10557558e` + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-node-linux-amd64.tar.gz) | `179278fecb65d246443f58cef00ca2f2a9d0ac6fbdb310994f0ac7fca249f7bdc1c79ea7f3e5455c1e2d2460f5447d006bfa579f97b502ee7034b2a1927f934a` +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-node-linux-arm64.tar.gz) | `01178703c84e0f671770e53024e3cc53f540c0cf93b0804d35884a777c3e3bc44c44d62b6fd25204348986fa589969a9255c0ef04235a0bb9d5560b09867aa0b` +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-node-linux-ppc64le.tar.gz) | `05d1ae963d5c4a382d380cb4f4cdfa924fa8a311953b5eaefe66b8696cebf14bffb13bda8ea784ca5fa1dd073c82ee148faa9a50911449cefad16fe2e800d7c1` +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-node-linux-s390x.tar.gz) | `b7501e91153d062c7c545ef9900faf9b29826b6ff5ec5320f6a799d3d3b479f6ae79092909a1905e055b72dd540a9c8fb02b2d0655f6957cd0b4b7b2e9c18909` +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0/kubernetes-node-windows-amd64.tar.gz) | `f54c606e8ecc29b4ba4ef4570f679352f66cbae1f1bd4f49db5e18227b00ed0e6d8dd47422390fd2a3b87d837cf39dae58a260208096169a3aabef9e874c7586` + +### 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.35.0](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.35.0](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.35.0](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.35.0](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.35.0](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.35.0](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.0 + +## Urgent Upgrade Notes + +### (No, really, you MUST read this before you upgrade) + +- ACTION REQUIRED: + + Removed the `--pod-infra-container-image` flag from `kubelet` command line. For `non-kubeadm` clusters, users must manually remove this flag from their `kubelet` configuration to prevent startup failures before upgrading `kubelet`. For `kubeadm` clusters, if users pass extra arguments to the `kubelet` like `--pod-infra-container-image`, it will be written to the `kubelet` env file during the `init` phase. `kubeadm` does not remove it during the `init` or `join` phase, so users must manually remove it from `extraArgs` in the `kubelet` configuration file. ([#133779](https://github.com/kubernetes/kubernetes/pull/133779), [@carlory](https://github.com/carlory)) + - ACTION REQUIRED: + + vendor: Updated `k8s.io/system-validators` to `v1.12.1`. The cgroups validator now throws an error instead of a warning if cgroups v1 is detected on the host and the provided KubeletVersion is `v1.35` or newer. + + kubeadm: Started using `k8s.io/system-validators` `v1.12.1` in `kubeadm` `v1.35`. During `kubeadm init`, `kubeadm join`, and `kubeadm upgrade`, the SystemVerification preflight check throws an error if cgroups v1 is detected and the detected `kubelet` version is `v1.35` or newer. For older versions of `kubelet`, a preflight warning is displayed. + + To allow cgroups v1 with `kubeadm` and `kubelet` version `v1.35` or newer, you must: + - Ignore the error from the SystemVerification preflight check by `kubeadm`. + - Edit the `kube-system/kubelet-config` ConfigMap and add the `failCgroupV1: false` field before upgrading. ([#134744](https://github.com/kubernetes/kubernetes/pull/134744), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle and Node] + +## Changes by Kind + +### Deprecation + +- ACTION REQUIRED: `failCgroupV1` will be set to true from 1.35. + This means that nodes will not start on a cgroup v1 by default. This puts cgroup v1 into a deprecated state. ([#134298](https://github.com/kubernetes/kubernetes/pull/134298), [@kannon92](https://github.com/kannon92)) +- Marked `ipvs` mode in kube-proxy as deprecated, which will be removed in a future version of Kubernetes. Users are encouraged to migrate to `nftables`. ([#134539](https://github.com/kubernetes/kubernetes/pull/134539), [@adrianmoisey](https://github.com/adrianmoisey)) + +### API Change + +- Added `ObservedGeneration` to CustomResourceDefinition conditions. ([#134984](https://github.com/kubernetes/kubernetes/pull/134984), [@michaelasp](https://github.com/michaelasp)) +- Added `WithOrigin` within `apis/core/validation` with adjusted tests. ([#132825](https://github.com/kubernetes/kubernetes/pull/132825), [@PatrickLaabs](https://github.com/PatrickLaabs)) +- Added scoring for the prioritized list feature so nodes that best satisfy the highest-ranked subrequests were chosen. ([#134711](https://github.com/kubernetes/kubernetes/pull/134711), [@mortent](https://github.com/mortent)) [SIG Node, Scheduling and Testing] +- Added the `--min-compatibility-version` flag to `kube-apiserver`, `kube-controller-manager`, and `kube-scheduler`. ([#133980](https://github.com/kubernetes/kubernetes/pull/133980), [@siyuanfoundation](https://github.com/siyuanfoundation)) [SIG API Machinery, Architecture, Cluster Lifecycle, Etcd, Scheduling and Testing] +- Added the `StorageVersionMigration` `v1beta1` API and removed the `v1alpha1` API. + + ACTION REQUIRED: The `v1alpha1` API is no longer supported. Users must remove any `v1alpha1` resources before upgrading. ([#134784](https://github.com/kubernetes/kubernetes/pull/134784), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Apps, Auth, Etcd and Testing] +- Added validation to ensure `log-flush-frequency` is a positive value, returning an error instead of causing a panic. ([#133540](https://github.com/kubernetes/kubernetes/pull/133540), [@BenTheElder](https://github.com/BenTheElder)) [SIG Architecture, Instrumentation, Network and Node] +- All containers are restarted when a source container in a restart policy rule exits. This alpha feature is gated behind `RestartAllContainersOnContainerExit`. ([#134345](https://github.com/kubernetes/kubernetes/pull/134345), [@yuanwang04](https://github.com/yuanwang04)) [SIG Apps, Node and Testing] +- CSI drivers can now opt in to receive service account tokens via the secrets field instead of volume context by setting `spec.serviceAccountTokenInSecrets: true` in the CSIDriver object. This prevents tokens from being exposed in logs and other outputs. The feature is gated by the `CSIServiceAccountTokenSecrets` feature gate (beta in `v1.35`). ([#134826](https://github.com/kubernetes/kubernetes/pull/134826), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth, Storage and Testing] +- Changed kuberc configuration schema. Two new optional fields added to kuberc configuration, `credPluginPolicy` and `credPluginAllowlist`. This is documented in [KEP-3104](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/3104-introduce-kuberc/README.md#allowlist-design-details) and documentation is added to the website by [kubernetes/website#52877](https://github.com/kubernetes/website/pull/52877) ([#134870](https://github.com/kubernetes/kubernetes/pull/134870), [@pmengelbert](https://github.com/pmengelbert)) [SIG API Machinery, Architecture, Auth, CLI, Instrumentation and Testing] +- DRA device taints: `DeviceTaintRule` status provides information about the rule, including whether Pods still need to be evicted (`EvictionInProgress` condition). The newly added `None` effect can be used to preview what a `DeviceTaintRule` would do if it used the `NoExecute` effect and to taint devices (`device health`) without immediately affecting scheduling or running Pods. ([#134152](https://github.com/kubernetes/kubernetes/pull/134152), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Node, Release, Scheduling and Testing] +- DRA: The `DynamicResourceAllocation` feature gate for the core functionality (GA in `v1.34`) has now been locked to enabled-by-default and cannot be disabled anymore. ([#134452](https://github.com/kubernetes/kubernetes/pull/134452), [@pohly](https://github.com/pohly)) [SIG Auth, Node, Scheduling and Testing] +- Enabled `kubectl get -o kyaml` by default. To disable it, set `KUBECTL_KYAML=false`. ([#133327](https://github.com/kubernetes/kubernetes/pull/133327), [@thockin](https://github.com/thockin)) +- Enabled in-place resizing of pod-level resources. + - Added `Resources` in `PodStatus` to capture resources set in the pod-level cgroup. + - Added `AllocatedResources` in `PodStatus` to capture resources requested in the `PodSpec`. ([#132919](https://github.com/kubernetes/kubernetes/pull/132919), [@ndixita](https://github.com/ndixita)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Instrumentation, Node, Scheduling and Testing] +- Enabled the `NominatedNodeNameForExpectation` feature in kube-scheduler by default. + - Enabled the `ClearingNominatedNodeNameAfterBinding` feature in kube-apiserver by default. ([#135103](https://github.com/kubernetes/kubernetes/pull/135103), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- Enhanced discovery responses to merge API groups and resources from all peer apiservers when the `UnknownVersionInteroperabilityProxy` feature is enabled. ([#133648](https://github.com/kubernetes/kubernetes/pull/133648), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Auth, Cloud Provider, Node, Scheduling and Testing] +- Extended `core/v1` `Toleration` to support numeric comparison operators (`Gt`,`Lt`). ([#134665](https://github.com/kubernetes/kubernetes/pull/134665), [@helayoty](https://github.com/helayoty)) [SIG API Machinery, Apps, Node, Scheduling, Testing and Windows] +- Feature gate dependencies are now explicit, and validated at startup. A feature can no longer be enabled if it depends on a disabled feature. In particular, this means that `AllAlpha=true` will no longer work without enabling disabled-by-default beta features that are depended on (either with `AllBeta=true` or explicitly enumerating the disabled dependencies). ([#133697](https://github.com/kubernetes/kubernetes/pull/133697), [@tallclair](https://github.com/tallclair)) [SIG API Machinery, Architecture, Cluster Lifecycle and Node] +- Generated OpenAPI model packages for API types into `zz_generated.model_name.go` files, accessible via the `OpenAPIModelName()` function. This allows API authors to declare desired OpenAPI model packages instead of relying on the Go package path of API types. ([#131755](https://github.com/kubernetes/kubernetes/pull/131755), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- Implemented constrained impersonation as described in [KEP-5284](https://kep.k8s.io/5284). ([#134803](https://github.com/kubernetes/kubernetes/pull/134803), [@enj](https://github.com/enj)) [SIG API Machinery, Auth and Testing] +- Introduced a new declarative validation tag `+k8s:customUnique` to control listmap uniqueness. ([#134279](https://github.com/kubernetes/kubernetes/pull/134279), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery and Auth] +- Introduced a structured and versioned `v1alpha1` response for the `statusz` endpoint. ([#134313](https://github.com/kubernetes/kubernetes/pull/134313), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- Introduced a structured and versioned `v1alpha1` response format for the `flagz` endpoint. ([#134995](https://github.com/kubernetes/kubernetes/pull/134995), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- Introduced the GangScheduling kube-scheduler plugin to support "all-or-nothing" scheduling using the `scheduling.k8s.io/v1alpha1` Workload API. ([#134722](https://github.com/kubernetes/kubernetes/pull/134722), [@macsko](https://github.com/macsko)) [SIG API Machinery, Apps, Auth, CLI, Etcd, Scheduling and Testing] +- Introduced the Node Declared Features capability (alpha), which includes: + - A new `Node.Status.DeclaredFeatures` field for publishing node-specific features. + - A `component-helpers` library for feature registration and inference. + - A `NodeDeclaredFeatures` scheduler plugin to match pods with nodes that provide required features. + - A `NodeDeclaredFeatureValidator` admission plugin to validate pod updates against a node's declared features. ([#133389](https://github.com/kubernetes/kubernetes/pull/133389), [@pravk03](https://github.com/pravk03)) [SIG API Machinery, Apps, Node, Release, Scheduling and Testing] +- Introduced the `scheduling.k8s.io/v1alpha1` Workload API to express workload-level scheduling requirements and allow the kube-scheduler to act on them. ([#134564](https://github.com/kubernetes/kubernetes/pull/134564), [@macsko](https://github.com/macsko)) [SIG API Machinery, Apps, CLI, Etcd, Scheduling and Testing] +- Introduced the alpha `MutableSchedulingDirectivesForSuspendedJobs` feature gate (disabled by default), which allows mutating a Job's scheduling directives while the Job is suspended. + It also updates the Job controller to clears the `status.startTime` field for suspended Jobs. ([#135104](https://github.com/kubernetes/kubernetes/pull/135104), [@mimowo](https://github.com/mimowo)) [SIG Apps and Testing] +- Kube-apiserver: Fixed a `v1.34` regression in `CustomResourceDefinition` handling that incorrectly warned about unrecognized formats on number and integer properties. ([#133896](https://github.com/kubernetes/kubernetes/pull/133896), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Contributor Experience, Network, Node and Scheduling] +- Kube-apiserver: Fixed a possible panic validating a custom resource whose `CustomResourceDefinition` indicates a status subresource exists, but which does not define a `status` property in the `openAPIV3Schema`. ([#133721](https://github.com/kubernetes/kubernetes/pull/133721), [@fusida](https://github.com/fusida)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Release, Scheduling, Storage and Testing] +- Kubernetes API Go types removed runtime use of the `github.com/gogo/protobuf` library, and are no longer registered into the global gogo type registry. Kubernetes API Go types were not suitable for use with the `google.golang.org/protobuf` library, and no longer implement `ProtoMessage()` by default to avoid accidental incompatible use. If removal of these marker methods impacts your use, it can be re-enabled for one more release with a `kubernetes_protomessage_one_more_release` build tag, but will be removed in `v1.36`. ([#134256](https://github.com/kubernetes/kubernetes/pull/134256), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] +- Made node affinity in Persistent Volume mutable. ([#134339](https://github.com/kubernetes/kubernetes/pull/134339), [@huww98](https://github.com/huww98)) [SIG API Machinery, Apps and Node] +- Moved the `ImagePullIntent` and `ImagePulledRecord` objects used by the kubelet to track image pulls to the `v1beta1` API version. ([#132579](https://github.com/kubernetes/kubernetes/pull/132579), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- Pod resize now only allows CPU and memory resources; other resource types are forbidden. ([#135084](https://github.com/kubernetes/kubernetes/pull/135084), [@tallclair](https://github.com/tallclair)) [SIG Apps, Node and Testing] +- Prevented Pods from being scheduled onto nodes that lack the required CSI driver. ([#135012](https://github.com/kubernetes/kubernetes/pull/135012), [@gnufied](https://github.com/gnufied)) [SIG API Machinery, Scheduling, Storage and Testing] +- Promoted HPA configurable tolerance to beta. The `HPAConfigurableTolerance` feature gate has now been enabled by default. ([#133128](https://github.com/kubernetes/kubernetes/pull/133128), [@jm-franc](https://github.com/jm-franc)) [SIG API Machinery and Autoscaling] +- Promoted ReplicaSet and Deployment `.status.terminatingReplicas` tracking to beta. The `DeploymentReplicaSetTerminatingReplicas` feature gate is now enabled by default. ([#133087](https://github.com/kubernetes/kubernetes/pull/133087), [@atiratree](https://github.com/atiratree)) [SIG API Machinery, Apps and Testing] +- Promoted `PodObservedGenerationTracking` to GA. ([#134948](https://github.com/kubernetes/kubernetes/pull/134948), [@natasha41575](https://github.com/natasha41575)) [SIG API Machinery, Apps, Node, Scheduling and Testing] +- Promoted the `JobManagedBy` feature to general availability. The `JobManagedBy` feature gate was locked to `true` and will be removed in a future Kubernetes release. ([#135080](https://github.com/kubernetes/kubernetes/pull/135080), [@dejanzele](https://github.com/dejanzele)) [SIG API Machinery, Apps and Testing] +- Promoted the `MaxUnavailableStatefulSet` feature to beta and enabling it by default. ([#133153](https://github.com/kubernetes/kubernetes/pull/133153), [@helayoty](https://github.com/helayoty)) [SIG API Machinery and Apps] +- Removed the `StrictCostEnforcementForVAP` and `StrictCostEnforcementForWebhooks` feature gates, which were locked since `v1.32`. ([#134994](https://github.com/kubernetes/kubernetes/pull/134994), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth, Node and Testing] +- Scheduler: Added the `bindingTimeout` argument to the DynamicResources plugin configuration, allowing customization of the wait duration in `PreBind` for device binding conditions. + Defaults to 10 minutes when `DRADeviceBindingConditions` and `DRAResourceClaimDeviceStatus` are both enabled. ([#134905](https://github.com/kubernetes/kubernetes/pull/134905), [@fj-naji](https://github.com/fj-naji)) [SIG Node and Scheduling] +- The DRA device taints and toleration feature received a separate feature gate, `DRADeviceTaintRules`, which controlled support for `DeviceTaintRules`. This allowed disabling it while keeping `DRADeviceTaints` enabled so that tainting via `ResourceSlices` continued to work. ([#135068](https://github.com/kubernetes/kubernetes/pull/135068), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Node, Scheduling and Testing] +- The Pod Certificates feature moved to beta. The `PodCertificateRequest` feature gate is set disabled by default. To use the feature, users must enable the certificates API groups in `v1beta1` and enable the `PodCertificateRequest` feature gate. The `UserAnnotations` field was added to the `PodCertificateProjection` API and the corresponding `UnverifiedUserAnnotations` field was added to the `PodCertificateRequest` API. ([#134624](https://github.com/kubernetes/kubernetes/pull/134624), [@yt2985](https://github.com/yt2985)) [SIG API Machinery, Apps, Auth, Etcd, Instrumentation, Node and Testing] +- The `KubeletEnsureSecretPulledImages` feature was promoted to Beta and enabled by default. ([#135228](https://github.com/kubernetes/kubernetes/pull/135228), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] +- The `PreferSameZone` and `PreferSameNode` values for the Service + `trafficDistribution` field graduated to general availability. The + `PreferClose` value is now deprecated in favor of the more explicit + `PreferSameZone`. ([#134457](https://github.com/kubernetes/kubernetes/pull/134457), [@danwinship](https://github.com/danwinship)) [SIG API Machinery, Apps, Network and Testing] +- Updated `ResourceQuota` to count device class requests within a `ResourceClaim` as two additional quotas when the `DRAExtendedResource` feature is enabled: + - `requests.deviceclass.resource.k8s.io/` is charged based on the worst-case number of devices requested. + - Device classes mapping to an extended resource now consume `requests.`. ([#134210](https://github.com/kubernetes/kubernetes/pull/134210), [@yliaog](https://github.com/yliaog)) [SIG API Machinery, Apps, Node, Scheduling and Testing] +- Updated storage version for `MutatingAdmissionPolicy` to `v1beta1`. ([#133715](https://github.com/kubernetes/kubernetes/pull/133715), [@cici37](https://github.com/cici37)) [SIG API Machinery, Etcd and Testing] +- Updated the Partitionable Devices feature to support referencing counter sets across ResourceSlices within the same resource pool. Devices from incomplete pools were no longer considered for allocation. This change introduced backwards-incompatible updates to the alpha feature, requiring any ResourceSlices using it to be removed before upgrading or downgrading between v1.34 and v1.35. ([#134189](https://github.com/kubernetes/kubernetes/pull/134189), [@mortent](https://github.com/mortent)) [SIG API Machinery, Node, Scheduling and Testing] +- Upgraded the `PodObservedGenerationTracking` feature to beta in `v1.34` and removed the alpha version description from the OpenAPI specification. ([#133883](https://github.com/kubernetes/kubernetes/pull/133883), [@yangjunmyfm192085](https://github.com/yangjunmyfm192085)) + +### Feature + +- Added `k8s-short-name` and `k8s-long-name` format validation tags to enforce DNS label and DNS subdomain compliance. ([#133894](https://github.com/kubernetes/kubernetes/pull/133894), [@lalitc375](https://github.com/lalitc375)) +- Added `kubectl kuberc view` and `kubectl kuberc set` commands to perform operations against the `kuberc` file. ([#135003](https://github.com/kubernetes/kubernetes/pull/135003), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] +- Added `kubelet` stress test for pod cleanup when rejection due to `VolumeAttachmentLimitExceeded`. ([#133357](https://github.com/kubernetes/kubernetes/pull/133357), [@torredil](https://github.com/torredil)) [SIG Node and Storage] +- Added `paths` section to kubelet `statusz` endpoint. ([#133239](https://github.com/kubernetes/kubernetes/pull/133239), [@Peac36](https://github.com/Peac36)) +- Added a `source` label to the `resourceclaim_controller_resource_claims` metric. + Added the `scheduler_resourceclaim_creates_total` metric for `DRAExtendedResource`. ([#134523](https://github.com/kubernetes/kubernetes/pull/134523), [@bitoku](https://github.com/bitoku)) [SIG Apps, Instrumentation, Node and Scheduling] +- Added a counter metric `kubelet_image_manager_ensure_image_requests_total{present_locally, pull_policy, pull_required}` that exposes details about `kubelet` ensuring an image exists on the node. ([#132644](https://github.com/kubernetes/kubernetes/pull/132644), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- Added additional event emissions during Pod resizing to provide clearer visibility when a Pod’s resize status changes. ([#134825](https://github.com/kubernetes/kubernetes/pull/134825), [@natasha41575](https://github.com/natasha41575)) +- Added configurable per-device health check timeouts to the DRA health monitoring API. ([#135147](https://github.com/kubernetes/kubernetes/pull/135147), [@harche](https://github.com/harche)) [SIG Node] +- Added metrics for the `MaxUnavailable` feature in `StatefulSet`. ([#130951](https://github.com/kubernetes/kubernetes/pull/130951), [@Edwinhr716](https://github.com/Edwinhr716)) [SIG Apps and Instrumentation] +- Added paths section to scheduler `statusz` endpoint. ([#132606](https://github.com/kubernetes/kubernetes/pull/132606), [@Peac36](https://github.com/Peac36)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- Added remote runtime and image `Close()` method to be able to close the connection. ([#133211](https://github.com/kubernetes/kubernetes/pull/133211), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node] +- Added support for tracing in `kubectl` with the `--profile=trace` flag. ([#134709](https://github.com/kubernetes/kubernetes/pull/134709), [@tchap](https://github.com/tchap)) +- Added support for validating UUID format. ([#133948](https://github.com/kubernetes/kubernetes/pull/133948), [@lalitc375](https://github.com/lalitc375)) +- Added the `-n` flag as a shorthand for `--namespace` in the `kubectl config set-context` command. ([#134384](https://github.com/kubernetes/kubernetes/pull/134384), [@tchap](https://github.com/tchap)) [SIG CLI and Testing] +- Added the `ChangeContainerStatusOnKubeletRestart` feature gate, which defaults to disabled. When the feature gate is disabled, `kubelet` does not change the Pod status upon restart, and Pods do not re-run startup probes after the `kubelet` restarts. ([#134746](https://github.com/kubernetes/kubernetes/pull/134746), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node and Testing] +- Added the `CloudControllerManagerWatchBasedRoutesReconciliation` feature gate. ([#131220](https://github.com/kubernetes/kubernetes/pull/131220), [@lukasmetzner](https://github.com/lukasmetzner)) [SIG API Machinery and Cloud Provider] +- Added the `UserNamespacesHostNetworkSupport` feature gate. This gate is disabled by default, and when enabled, allowed `hostNetwork` pods to use user namespaces. ([#134893](https://github.com/kubernetes/kubernetes/pull/134893), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Apps, Node and Testing] +- After fixing regressions detected in `v1.34`, the `SchedulerAsyncAPICalls` feature gate was re-enabled by default. ([#135059](https://github.com/kubernetes/kubernetes/pull/135059), [@macsko](https://github.com/macsko)) +- Changed `WaitForNamedCacheSync` to `WaitForNamedCacheSyncWithContext`. ([#133904](https://github.com/kubernetes/kubernetes/pull/133904), [@aditigupta96](https://github.com/aditigupta96)) [SIG API Machinery, Apps, Auth and Network] +- DRA: the resource.k8s.io API now uses the v1 API version (introduced in 1.34) as default storage version. Downgrading to 1.33 is not supported. ([#133876](https://github.com/kubernetes/kubernetes/pull/133876), [@kei01234kei](https://github.com/kei01234kei)) [SIG API Machinery, Etcd and Testing] +- Enabled the `MutableCSINodeAllocatableCount` feature gate by default in beta. ([#134647](https://github.com/kubernetes/kubernetes/pull/134647), [@torredil](https://github.com/torredil)) +- Enabled the `WatchListClient` feature gate. ([#134180](https://github.com/kubernetes/kubernetes/pull/134180), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery, Apps, Auth, CLI, Instrumentation, Node and Testing] +- Enabled the feature gate `ContainerRestartRules` by default. The `ContainerRestartRules` feature has been promoted to beta. Fixed a bug in this feature that caused probes to continue to run even if the container has terminated and is not restartable. ([#134631](https://github.com/kubernetes/kubernetes/pull/134631), [@yuanwang04](https://github.com/yuanwang04)) +- Graduated the `PodTopologyLabelsAdmission` feature gate to Beta and enabled it by default. + Pods now receive `topology.kubernetes.io/zone` and `topology.kubernetes.io/region` labels automatically when their assigned Node has these labels. ([#135158](https://github.com/kubernetes/kubernetes/pull/135158), [@andrewsykim](https://github.com/andrewsykim)) +- Graduated the fine-grained supplemental groups policy (KEP-3619) to GA. ([#135088](https://github.com/kubernetes/kubernetes/pull/135088), [@everpeace](https://github.com/everpeace)) [SIG Node and Testing] +- Graduated the image volume source feature to Beta and enabled it by default. ([#135195](https://github.com/kubernetes/kubernetes/pull/135195), [@haircommander](https://github.com/haircommander)) [SIG Apps, Instrumentation, Node and Testing] +- Implemented opportunistic batching (KEP-5598) to optimize scheduling for pods with identical scheduling requirements. ([#135231](https://github.com/kubernetes/kubernetes/pull/135231), [@bwsalmon](https://github.com/bwsalmon)) [SIG Node, Scheduling, Storage and Testing] +- Implemented scoring for DRA-backed extended resources. ([#134058](https://github.com/kubernetes/kubernetes/pull/134058), [@bart0sh](https://github.com/bart0sh)) [SIG Node, Scheduling and Testing] +- Improved throughput in the `real-FIFO` queue used by `informers` and `controllers` by adding batch handling for processing watch events. ([#132240](https://github.com/kubernetes/kubernetes/pull/132240), [@yue9944882](https://github.com/yue9944882)) [SIG API Machinery, Scheduling and Storage] +- Introduced end-to-end tests to verify component invariant metrics across the entire test suite. ([#133394](https://github.com/kubernetes/kubernetes/pull/133394), [@BenTheElder](https://github.com/BenTheElder)) +- Introduced new kubelet metrics for the Ensure Secret Pulled Images KEP, including: + - `kubelet_imagemanager_ondisk_pullintents` for tracking pull intent records on disk + - `kubelet_imagemanager_ondisk_pulledrecords` for tracking pulled image records on disk + - `kubelet_imagemanager_image_mustpull_checks_total{result}` for counting image must-pull verification checks. ([#132812](https://github.com/kubernetes/kubernetes/pull/132812), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- Introduced the `--as-user-extra` persistent flag in `kubectl`, which allows passing extra arguments during impersonation. ([#134378](https://github.com/kubernetes/kubernetes/pull/134378), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] +- K8s.io/apimachinery: Introduced a helper function to compare `resourceVersion` strings between two objects of the same resource. ([#134330](https://github.com/kubernetes/kubernetes/pull/134330), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Apps, Auth, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- KEP-5440: Enabled support for resizing resources while a Job is suspended. This feature is alpha. ([#132441](https://github.com/kubernetes/kubernetes/pull/132441), [@kannon92](https://github.com/kannon92)) [SIG Apps and Testing] +- Kube-apiserver: Made the subresources `pods/exec`, `pods/attach`, and `pods/portforward` require `create` permission for both SPDY and Websocket API requests. Previously, SPDY requests required `create` permission, but Websocket requests only required `get` permission. This change is gated by the `AuthorizePodWebsocketUpgradeCreatePermission` feature-gate, which is enabled by default. + + Before upgrading to 1.35, ensure any custom ClusterRoles and Roles intended to grant `pods/exec`, `pods/attach`, or `pods/portforward` permission include the `create` verb. ([#134577](https://github.com/kubernetes/kubernetes/pull/134577), [@seans3](https://github.com/seans3)) [SIG API Machinery, Auth, Node and Testing] +- Kubeadm: Added error printing during retries related to the `WaitForAllControlPlaneComponents` functionality at verbosity level 5. ([#134433](https://github.com/kubernetes/kubernetes/pull/134433), [@neolit123](https://github.com/neolit123)) +- Kubeadm: Added the `HTTPEndpoints` field to `ClusterConfiguration.Etcd.ExternalEtcd` to configure HTTP endpoints for etcd communication in v1beta4. This separates HTTP traffic (e.g., `/metrics`, `/health`) from gRPC traffic, improving access control. Mirrors etcd’s `--listen-client-http-urls` behavior; if not set, the `Endpoints` field handles both traffic types. ([#134890](https://github.com/kubernetes/kubernetes/pull/134890), [@SataQiu](https://github.com/SataQiu)) +- Kubeadm: Graduated the kubeadm-specific feature gate `ControlPlaneKubeletLocalMode` to GA and locked it to enabled by default. To opt out, patch the `server` field in `/etc/kubernetes/kubelet.conf`. Deprecated the subphase of `kubeadm join phase control-plane-join` called `etcd`, which is now hidden and replaced by subphase with identical functionality `etcd-join`. The `etcd` subphase will be removed in a future release. The subphase `kubelet-wait-bootstrap` of `kubeadm join` is no longer experimental and will now always run. ([#134106](https://github.com/kubernetes/kubernetes/pull/134106), [@neolit123](https://github.com/neolit123)) +- Kubernetes is now built using Go 1.25.1 ([#134095](https://github.com/kubernetes/kubernetes/pull/134095), [@dims](https://github.com/dims)) [SIG Release and Testing] +- Kubernetes is now built using Go 1.25.4 ([#135492](https://github.com/kubernetes/kubernetes/pull/135492), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] +- Kubernetes now uses Go Language Version 1.25, including https://go.dev/blog/container-aware-gomaxprocs ([#134120](https://github.com/kubernetes/kubernetes/pull/134120), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Release, Scheduling and Storage] +- Locked down the `AllowOverwriteTerminationGracePeriodSeconds` feature gate. ([#133792](https://github.com/kubernetes/kubernetes/pull/133792), [@HirazawaUi](https://github.com/HirazawaUi)) +- Locked the (generally available) feature gate `ExecProbeTimeout` to true. ([#134635](https://github.com/kubernetes/kubernetes/pull/134635), [@vivzbansal](https://github.com/vivzbansal)) [SIG Node and Testing] +- Metrics: Excluded `dryRun` requests from `apiserver_request_sli_duration_seconds`. ([#131092](https://github.com/kubernetes/kubernetes/pull/131092), [@aldudko](https://github.com/aldudko)) [SIG API Machinery and Instrumentation] +- Migrated validation in `resource.k8s.io` to declarative validation. + When the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. + when `DeclarativeValidationTakeover` feature gate is enabled, declarative validation becomes the primary source of errors for migrated fields. ([#134072](https://github.com/kubernetes/kubernetes/pull/134072), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps and Auth] +- Moved the Pod Certificates feature to beta. Added `UserAnnotations` to the `PodCertificateProjection` API and `UnverifiedUserAnnotations` to the `PodCertificateRequest` API. The `PodCertificateRequest` feature gate remains disabled by default and requires enabling the v1beta1 certificates API groups. ([#134790](https://github.com/kubernetes/kubernetes/pull/134790), [@yt2985](https://github.com/yt2985)) [SIG Auth, Instrumentation and Testing] +- Promoted `ImageGCMaximumAge` to stable. ([#134736](https://github.com/kubernetes/kubernetes/pull/134736), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] +- Promoted `InPlacePodVerticalScaling` to GA. ([#134949](https://github.com/kubernetes/kubernetes/pull/134949), [@natasha41575](https://github.com/natasha41575)) [SIG API Machinery, Node and Scheduling] +- Promoted `kubectl` command headers to stable. ([#134777](https://github.com/kubernetes/kubernetes/pull/134777), [@soltysh](https://github.com/soltysh)) [SIG CLI and Testing] +- Promoted the `EnvFiles` feature gate to beta and is enabled by default. Additionally, the syntax specification for environment variables has been restricted to a subset of POSIX shell syntax (all variable values must be wrapped in single quotes). ([#134414](https://github.com/kubernetes/kubernetes/pull/134414), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node and Testing] +- Promoted the `HostnameOverride` feature gate to beta and enabled it by default. ([#134729](https://github.com/kubernetes/kubernetes/pull/134729), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Network and Node] +- Promoted the `KubeletCrashLoopBackOffMax` feature gate to beta and enabled it by default. ([#135044](https://github.com/kubernetes/kubernetes/pull/135044), [@hankfreund](https://github.com/hankfreund)) +- Selected a single device class deterministically when multiple device classes were available for an extended resource. ([#135037](https://github.com/kubernetes/kubernetes/pull/135037), [@yliaog](https://github.com/yliaog)) [SIG Node, Scheduling and Testing] +- The JWT authenticator in `kube-apiserver` now reports the following metrics when the `StructuredAuthenticationConfiguration` feature gate is enabled: + - `apiserver_authentication_jwt_authenticator_jwks_fetch_last_timestamp_seconds` + - `apiserver_authentication_jwt_authenticator_jwks_fetch_last_key_set_info`. ([#123642](https://github.com/kubernetes/kubernetes/pull/123642), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] +- The scheduler now clears the `nominatedNodeName` field for Pods upon scheduling or binding failure. External components, such as Cluster Autoscaler and Karpenter, should not overwrite this field. ([#135007](https://github.com/kubernetes/kubernetes/pull/135007), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] +- Updated `applyconfiguration-gen` to generate extract functions for all subresources. ([#132665](https://github.com/kubernetes/kubernetes/pull/132665), [@mrIncompetent](https://github.com/mrIncompetent)) +- Updated `applyconfiguration-gen` to preserve struct and field comments from source types in the generated code. ([#132663](https://github.com/kubernetes/kubernetes/pull/132663), [@mrIncompetent](https://github.com/mrIncompetent)) +- Updated `kubectl describe pods` to include the involved object’s `fieldPath` (e.g., container name) in event messages, providing better context for debugging multi-container Pods. Note: This changes the previous message format for events that include a `fieldPath`. ([#133627](https://github.com/kubernetes/kubernetes/pull/133627), [@itzPranshul](https://github.com/itzPranshul)) +- Updated sandbox ordering to use by attempt count or creation time. ([#130551](https://github.com/kubernetes/kubernetes/pull/130551), [@yylt](https://github.com/yylt)) +- Updated the Kubernetes build to use Go `1.25.4`. ([#135187](https://github.com/kubernetes/kubernetes/pull/135187), [@BenTheElder](https://github.com/BenTheElder)) +- Updated underlying images and dependencies to be compatible with Go version`1.25.3`. ([#134611](https://github.com/kubernetes/kubernetes/pull/134611), [@cpanato](https://github.com/cpanato)) [SIG Architecture, Cloud Provider, Etcd, Release, Storage and Testing] +- `kubeadm`: Added a preflight check `ContainerRuntimeVersion` to validate if the installed container runtime supports the `RuntimeConfig` gRPC method. If unsupported, `kubeadm` prints a warning message. + + Starting with Kubernetes `v1.36`, `kubelet` might refuse to start if the CRI runtime does not support this feature. More information can be found at the [Kubernetes blog](https://kubernetes.io/blog/2025/09/12/kubernetes-v1-34-cri-cgroup-driver-lookup-now-ga/). ([#134906](https://github.com/kubernetes/kubernetes/pull/134906), [@carlory](https://github.com/carlory)) + +- Kubernetes is now built using Go `1.25.5`. ([#135609](https://github.com/kubernetes/kubernetes/pull/135609), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] + +### Documentation + +- Promoted the `--chunk-size` flag to stable. The kubectl `describe`, `get`, `drain`, and `events` commands can use `--chunk-size` flag to set chunk size. ([#134481](https://github.com/kubernetes/kubernetes/pull/134481), [@soltysh](https://github.com/soltysh)) + +### Bug or Regression + +- Added support for Pods to reference the same `PersistentVolumeClaim` across multiple volumes. ([#122140](https://github.com/kubernetes/kubernetes/pull/122140), [@huww98](https://github.com/huww98)) [SIG Node, Storage and Testing] +- Added support for the `ShareID` field of the `DRAConsumableCapacity` feature in the Kubelet Plugin API. ([#134520](https://github.com/kubernetes/kubernetes/pull/134520), [@sunya-ch](https://github.com/sunya-ch)) [SIG Node and Testing] +- Added the correct error when eviction is blocked due to the failSafe mechanism of the `DisruptionController`. ([#133097](https://github.com/kubernetes/kubernetes/pull/133097), [@kei01234kei](https://github.com/kei01234kei)) [SIG Apps and Node] +- Changed `kubectl exec` syntax to require `--` before the command. The form `kubectl exec [POD] [COMMAND]` is no longer supported; use `kubectl exec [POD] -- [COMMAND]` instead. ([#133841](https://github.com/kubernetes/kubernetes/pull/133841), [@yangjunmyfm192085](https://github.com/yangjunmyfm192085)) +- DRA API: Fixed the `tolerations` field in exact and sub requests to drop properly when the `DRADeviceTaints` API is disabled. ([#132927](https://github.com/kubernetes/kubernetes/pull/132927), [@pohly](https://github.com/pohly)) +- DRA Device Taints: Fixed toleration of `NoExecute`. Prior to this enhancement, tolerating a `NoExecute` did not work because the scheduler did not inform the eviction controller about the toleration, so the scheduled pod got evicted almost immediately. ([#134479](https://github.com/kubernetes/kubernetes/pull/134479), [@pohly](https://github.com/pohly)) [SIG Apps, Node, Scheduling and Testing] +- Deprecated metrics will be hidden as per the metrics deprecation policy. https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecating-a-metric . ([#133436](https://github.com/kubernetes/kubernetes/pull/133436), [@richabanker](https://github.com/richabanker)) [SIG Architecture, Instrumentation and Network] +- Disabled the `SchedulerAsyncAPICalls` feature gate to mitigate a bug where its interaction with asynchronous preemption could degrade `kube-scheduler` performance, especially under high `kube-apiserver` load. ([#134400](https://github.com/kubernetes/kubernetes/pull/134400), [@macsko](https://github.com/macsko)) +- Dropped `DeviceBindingConditions` fields when the `DRADeviceBindingConditions` feature gate is not enabled and not in use. ([#134964](https://github.com/kubernetes/kubernetes/pull/134964), [@sunya-ch](https://github.com/sunya-ch)) +- Extended resources requested by initContainers which are allocated using an automatic ResourceClaim now match the behavior of legacy device plugins, reusing the same resources requested by later sidecar initContainers or regular containers when possible, to minimize the total number of devices requested by the pod. ([#134882](https://github.com/kubernetes/kubernetes/pull/134882), [@yliaog](https://github.com/yliaog)) [SIG Apps, CLI, Node, Scheduling and Testing] +- Fixed SELinux warning controller not emitting events on some SELinux label conflicts. ([#133425](https://github.com/kubernetes/kubernetes/pull/133425), [@jsafrane](https://github.com/jsafrane)) [SIG Apps, Storage and Testing] +- Fixed `replicaCount` calculation exceeding max `int32`. ([#126979](https://github.com/kubernetes/kubernetes/pull/126979), [@omerap12](https://github.com/omerap12)) [SIG Apps and Autoscaling] +- Fixed a 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. ([#135146](https://github.com/kubernetes/kubernetes/pull/135146), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] +- Fixed a bug in `ValidatingAdmissionPolicy` where schemas with `additionalProperties: true` could cause the kube-controller-manager to crash with a nil pointer exception. ([#135155](https://github.com/kubernetes/kubernetes/pull/135155), [@jpbetz](https://github.com/jpbetz)) +- Fixed a bug in `kube-proxy` `nftables` mode (GA as of `v1.33`) which 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. ([#134024](https://github.com/kubernetes/kubernetes/pull/134024), [@jack4it](https://github.com/jack4it)) +- Fixed a bug in `kube-scheduler` where pending pod preemption caused preemptor pods to be retried more frequently. ([#134245](https://github.com/kubernetes/kubernetes/pull/134245), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] +- Fixed a bug that caused apiservers to send an inappropriate Content-Type request header to authorization, token authentication, imagepolicy admission, and audit webhooks when the alpha client-go feature gate "ClientsPreferCBOR" is enabled. ([#132960](https://github.com/kubernetes/kubernetes/pull/132960), [@benluddy](https://github.com/benluddy)) [SIG API Machinery and Node] +- Fixed a bug that caused duplicate validation when updating `PersistentVolumeClaims`, `VolumeAttachments` and `VolumeAttributesClasses`. ([#132549](https://github.com/kubernetes/kubernetes/pull/132549), [@gavinkflam](https://github.com/gavinkflam)) +- Fixed a bug that caused duplicate validation when updating `Role` and `RoleBinding` resources. ([#132550](https://github.com/kubernetes/kubernetes/pull/132550), [@gavinkflam](https://github.com/gavinkflam)) +- Fixed a bug that prevented allocating the same device that was previously consuming the `CounterSet` when both `DRAConsumableCapacity` and `DRAPartitionableDevices` were enabled. ([#134103](https://github.com/kubernetes/kubernetes/pull/134103), [@sunya-ch](https://github.com/sunya-ch)) +- Fixed a bug that prevents scheduling the next pod when using the `DRAConsumableCapacity` feature. ([#133706](https://github.com/kubernetes/kubernetes/pull/133706), [@sunya-ch](https://github.com/sunya-ch)) +- Fixed a bug to prevent segmentation fault from occurring when updating deeply nested JSON fields. ([#134381](https://github.com/kubernetes/kubernetes/pull/134381), [@kon-angelo](https://github.com/kon-angelo)) [SIG API Machinery and CLI] +- Fixed a bug where 64-bit IPv6 `ServiceCIDRs` allocated addresses outside the subnet range. ([#134193](https://github.com/kubernetes/kubernetes/pull/134193), [@hoskeri](https://github.com/hoskeri)) +- Fixed a bug where Job status updates fail after resuming a Job that was previously started and suspended. + The error message was: `status.startTime: Required value: startTime cannot be removed for unsuspended job`. ([#134769](https://github.com/kubernetes/kubernetes/pull/134769), [@dejanzele](https://github.com/dejanzele)) [SIG Apps and Testing] +- Fixed a bug where `AllocationMode: All` would not succeed if a resource pool contained `ResourceSlices` that were not targeting the current node. ([#134466](https://github.com/kubernetes/kubernetes/pull/134466), [@mortent](https://github.com/mortent)) +- Fixed a bug where a deleted Pod in the binding phase continued to occupy space on the node in `kube-scheduler`. ([#134157](https://github.com/kubernetes/kubernetes/pull/134157), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] +- Fixed a bug where high latency `kube-apiserver` caused scheduling throughput degradation. ([#134154](https://github.com/kubernetes/kubernetes/pull/134154), [@macsko](https://github.com/macsko)) +- Fixed a bug where the health of a DRA resource was not reported in the Pod status if the resource claim was generated from a template or used a different local name in the Pod spec. ([#134875](https://github.com/kubernetes/kubernetes/pull/134875), [@Jpsassine](https://github.com/Jpsassine)) [SIG Node and Testing] +- Fixed a long-standing issue where `kubelet` rejected Pods with `NodeAffinityFailed` due to a stale informer cache. ([#134445](https://github.com/kubernetes/kubernetes/pull/134445), [@natasha41575](https://github.com/natasha41575)) +- Fixed a panic in `kubectl api-resources` that occurred when the Discovery Client failed. ([#134833](https://github.com/kubernetes/kubernetes/pull/134833), [@rikatz](https://github.com/rikatz)) +- Fixed a possible data race during metrics registration. ([#134390](https://github.com/kubernetes/kubernetes/pull/134390), [@liggitt](https://github.com/liggitt)) [SIG Architecture and Instrumentation] +- Fixed a spurious `namespace not found` error in default `v1.30+` configurations when using `ValidatingAdmissionPolicy` or `MutatingAdmissionPolicy` to intercept namespaced objects in newly-created namespaces. ([#135359](https://github.com/kubernetes/kubernetes/pull/135359), [@liggitt](https://github.com/liggitt)) +- Fixed a startup probe race condition that caused main containers to remain stuck in "Initializing" state when sidecar containers with startup probes had failed initially but succeeded on restart in pods with `restartPolicy=Never`. ([#133072](https://github.com/kubernetes/kubernetes/pull/133072), [@AadiDev005](https://github.com/AadiDev005)) [SIG Node and Testing] +- Fixed an issue in asynchronous preemption: Scheduler now checks if preemption is ongoing for a Pod before initiating new preemption calls. ([#134730](https://github.com/kubernetes/kubernetes/pull/134730), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] +- Fixed an issue that prevented restart policies and restart rules from being applied to static pods. ([#135031](https://github.com/kubernetes/kubernetes/pull/135031), [@yuanwang04](https://github.com/yuanwang04)) +- Fixed an issue where requests for a config `FromClass` in the `ResourceClaim` status were not referenced. ([#134793](https://github.com/kubernetes/kubernetes/pull/134793), [@LionelJouin](https://github.com/LionelJouin)) +- Fixed an issue where the `kubelet` `/configz` endpoint reported an incorrect value for `kubeletconfig.cgroupDriver` when the cgroup driver setting was received from the container runtime. ([#134743](https://github.com/kubernetes/kubernetes/pull/134743), [@marquiz](https://github.com/marquiz)) +- Fixed an issue where the default `serviceCIDR` controller did not log events because the event broadcaster was shutdown during initialization. ([#133338](https://github.com/kubernetes/kubernetes/pull/133338), [@aojea](https://github.com/aojea)) +- Fixed an issue with setting `distinctAttribute=nil` when the `DRAConsumableCapacity` feature gate is disabled. ([#134962](https://github.com/kubernetes/kubernetes/pull/134962), [@sunya-ch](https://github.com/sunya-ch)) +- Fixed broken shell completion for API resources. ([#133771](https://github.com/kubernetes/kubernetes/pull/133771), [@marckhouzam](https://github.com/marckhouzam)) +- Fixed incorrect behavior of preemptor pod when preemption of the victim takes long to complete. The preemptor pod should not be circling in scheduling cycles until preemption is finished. ([#134294](https://github.com/kubernetes/kubernetes/pull/134294), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] +- Fixed missing `kubelet_volume_stats_*` metrics. ([#133890](https://github.com/kubernetes/kubernetes/pull/133890), [@huww98](https://github.com/huww98)) [SIG Instrumentation and Node] +- Fixed occasional schedule delays when a static `PersistentVolume` is created. ([#133929](https://github.com/kubernetes/kubernetes/pull/133929), [@huww98](https://github.com/huww98)) [SIG Scheduling and Storage] +- Fixed resource claims deallocation for extended resource when Pod completes. ([#134312](https://github.com/kubernetes/kubernetes/pull/134312), [@alaypatel07](https://github.com/alaypatel07)) [SIG Apps, Node and Testing] +- Fixed the kubelet to honor the `userNamespaces.idsPerPod` configuration, which was previously ignored. ([#133373](https://github.com/kubernetes/kubernetes/pull/133373), [@AkihiroSuda](https://github.com/AkihiroSuda)) [SIG Node and Testing] +- Fixed the replacement tag in APIs so it no longer acted as a selector for storage version. ([#135197](https://github.com/kubernetes/kubernetes/pull/135197), [@Jefftree](https://github.com/Jefftree)) +- Fixed validation error when `ConfigFlags` includes `CertFile` and/or `KeyFile` while the original configuration also contains `CertFileData` and/or `KeyFileData`. ([#133917](https://github.com/kubernetes/kubernetes/pull/133917), [@n2h9](https://github.com/n2h9)) [SIG API Machinery and CLI] +- Improved performance of `Endpoint` and `EndpointSlice` controllers when there are a large number of services in a single namespace by making pod-to-service lookup asynchronous. ([#134739](https://github.com/kubernetes/kubernetes/pull/134739), [@shyamjvs](https://github.com/shyamjvs)) [SIG Apps and Network] +- Improved the `FreeDiskSpaceFailed` warning event to provide more actionable details when image garbage collection fails to free enough disk space. Example: `Insufficient free disk space on the node's image filesystem (95.0% of 10.0 GiB used). Failed to free sufficient space by deleting unused images. Consider resizing the disk or deleting unused files.`. ([#132578](https://github.com/kubernetes/kubernetes/pull/132578), [@drigz](https://github.com/drigz)) +- Introduced support for using an implicit extended resource name derived from the device class (`deviceclass.resource.kubernetes.io/`) to request DRA devices matching that class. ([#133363](https://github.com/kubernetes/kubernetes/pull/133363), [@yliaog](https://github.com/yliaog)) [SIG Node, Scheduling and Testing] +- Kube-apiserver: Fixed a `v1.34` regression with spurious "Error getting keys" log messages. ([#133817](https://github.com/kubernetes/kubernetes/pull/133817), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] +- Kube-apiserver: Fixed a possible `v1.34` performance regression calculating object size statistics for resources not served from the watch cache, typically only `Events`. ([#133873](https://github.com/kubernetes/kubernetes/pull/133873), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] +- Kube-apiserver: Improved validation error messages for custom resources with CEL validation rules to include the value that failed validation. ([#132798](https://github.com/kubernetes/kubernetes/pull/132798), [@cbandy](https://github.com/cbandy)) +- Kube-apiserver: Made sure that when `--requestheader-client-ca-file` and `--client-ca-file` contain overlapping certificates, `--requestheader-allowed-names` must be specified so that regular client certificates cannot set authenticating proxy headers for arbitrary users. ([#131411](https://github.com/kubernetes/kubernetes/pull/131411), [@ballista01](https://github.com/ballista01)) [SIG API Machinery, Auth and Security] +- Kube-apiserver: Resolved an issue causing unnecessary warning log messages about enabled alpha APIs during API server startup. ([#135327](https://github.com/kubernetes/kubernetes/pull/135327), [@michaelasp](https://github.com/michaelasp)) +- Kube-controller-manager: Fixed a possible data race in the garbage collection controller. ([#134379](https://github.com/kubernetes/kubernetes/pull/134379), [@liggitt](https://github.com/liggitt)) [SIG API Machinery and Apps] +- Kube-controller-manager: Resolved potential issues handling pods with incorrect uids in their `ownerReference`. ([#134654](https://github.com/kubernetes/kubernetes/pull/134654), [@liggitt](https://github.com/liggitt)) +- Kubeadm: Added missing cluster-info context validation to prevent panics when the user has a malformed kubeconfig in the cluster-info ConfigMap that excludes a valid current context. ([#134715](https://github.com/kubernetes/kubernetes/pull/134715), [@neolit123](https://github.com/neolit123)) +- 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. ([#134265](https://github.com/kubernetes/kubernetes/pull/134265), [@neolit123](https://github.com/neolit123)) +- Kubeadm: Fixed `KUBEADM_UPGRADE_DRYRUN_DIR` not honored in upgrade phase when writing kubelet config files. ([#134007](https://github.com/kubernetes/kubernetes/pull/134007), [@carlory](https://github.com/carlory)) +- Kubeadm: Fixed a bug where `ClusterConfiguration.APIServer.TimeoutForControlPlane` from `v1beta3` was not respected in newer kubeadm versions where `v1beta4` is the default. ([#133513](https://github.com/kubernetes/kubernetes/pull/133513), [@tom1299](https://github.com/tom1299)) +- 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. ([#134319](https://github.com/kubernetes/kubernetes/pull/134319), [@neolit123](https://github.com/neolit123)) +- Kubeadm: Fixed a preflight check that could fail hostname construction in IPv6 setups. ([#134588](https://github.com/kubernetes/kubernetes/pull/134588), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth, Cloud Provider, Cluster Lifecycle and Testing] +- Kubelet: Fixed a concurrent map write error when creating a pod with an empty volume while the `LocalStorageCapacityIsolationFSQuotaMonitoring` feature gate is enabled. ([#135174](https://github.com/kubernetes/kubernetes/pull/135174), [@carlory](https://github.com/carlory)) +- Kubelet: Fixed an internal deadlock that caused the connection to a DRA driver to become unusable after being idle for 30 minutes. ([#133926](https://github.com/kubernetes/kubernetes/pull/133926), [@pohly](https://github.com/pohly)) +- Made legacy watch calls (`ResourceVersion` = 0 or unset) that generate init-events weigh higher in `API Priority and Fairness (APF)` seat usage. Properly accounting for their cost protects the API server from CPU overload. Users might see increased throttling of such calls as a result. ([#134601](https://github.com/kubernetes/kubernetes/pull/134601), [@shyamjvs](https://github.com/shyamjvs)) +- Namespace is now included in the `--dry-run=client` output for `HorizontalPodAutoscaler (HPA)` objects. ([#134263](https://github.com/kubernetes/kubernetes/pull/134263), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] +- Populated `involvedObject.apiVersion` on Events created for Nodes and Pods. ([#134545](https://github.com/kubernetes/kubernetes/pull/134545), [@novahe](https://github.com/novahe)) [SIG Cloud Provider, Network, Node, Scalability and Testing] +- Promoted VAC API test to conformance. ([#133615](https://github.com/kubernetes/kubernetes/pull/133615), [@carlory](https://github.com/carlory)) [SIG Architecture, Storage and Testing] +- Removed `BlockOwnerDeletion` from `ResourceClaim` created from `ResourceClaimTemplate` and from `extendedResourceClaim` created by the `scheduler`. ([#134956](https://github.com/kubernetes/kubernetes/pull/134956), [@yliaog](https://github.com/yliaog)) [SIG Apps, Node and Scheduling] +- Removed an incorrect `SessionAffinity` warning that appeared when a headless service was created or updated. ([#134054](https://github.com/kubernetes/kubernetes/pull/134054), [@Peac36](https://github.com/Peac36)) +- Slow container runtime initialization no longer causes the System WatchDog to kill the kubelet. The Device Manager was treated as unhealthy until it began listening on its port. ([#135153](https://github.com/kubernetes/kubernetes/pull/135153), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) +- Typed workqueue now cleans up goroutines before shutting down ([#135072](https://github.com/kubernetes/kubernetes/pull/135072), [@Jefftree](https://github.com/Jefftree)) [SIG API Machinery] +- Updated `kubectl scale` to return a consistent error message when a specified resource is not found. Previously, it returned: `error: no objects passed to scale "" not found`. It now matches the format used by other commands (e.g., `kubectl get`): `Error from server (NotFound): "" not found`. ([#134017](https://github.com/kubernetes/kubernetes/pull/134017), [@mochizuki875](https://github.com/mochizuki875)) +- `kube-controller-manager`: Fixed a `v1.34` regression that triggered a spurious rollout of existing StatefulSets when upgrading the control plane from `v1.33` to `v1.34`. This fix is guarded by the `StatefulSetSemanticRevisionComparison` feature gate, which is enabled by default. ([#135017](https://github.com/kubernetes/kubernetes/pull/135017), [@liggitt](https://github.com/liggitt)) +- `kube-scheduler`: Pod statuses no longer include specific taint keys or values when scheduling fails due to untolerated taints. ([#134740](https://github.com/kubernetes/kubernetes/pull/134740), [@hoskeri](https://github.com/hoskeri)) +- Fixes a bug where `MutatingAdmissionPolicy` would fail to apply to objects with duplicate list items (like env vars). ([#135560](https://github.com/kubernetes/kubernetes/pull/135560), [@lalitc375](https://github.com/lalitc375) [SIG API Machinery] +- K8s.io/client-go: Fixes a regression in 1.34+ which prevented informers from using configured Transformer functions. ([#135580](https://github.com/kubernetes/kubernetes/pull/135580), [@serathius](https://github.com/serathius) [SIG API Machinery] + +### Other (Cleanup or Flake) + +- Added the `Step` field to the testing framework to allow volume expansion in configurable step sizes for tests. ([#134760](https://github.com/kubernetes/kubernetes/pull/134760), [@Rishita-Golla](https://github.com/Rishita-Golla)) [SIG Storage and Testing] +- Bumped addon manager to use `kubectl` version `v1.32.2`. ([#130548](https://github.com/kubernetes/kubernetes/pull/130548), [@Jefftree](https://github.com/Jefftree)) [SIG Cloud Provider, Scalability and Testing] +- Dropped support for `certificates/v1beta1` `CertificateSigningRequest` in `kubectl`. ([#134782](https://github.com/kubernetes/kubernetes/pull/134782), [@scaliby](https://github.com/scaliby)) +- Dropped support for `discovery/v1beta1` `EndpointSlice` in `kubectl`. ([#134913](https://github.com/kubernetes/kubernetes/pull/134913), [@scaliby](https://github.com/scaliby)) +- Dropped support for `networking/v1beta1` `Ingress` in `kubectl`. ([#135108](https://github.com/kubernetes/kubernetes/pull/135108), [@scaliby](https://github.com/scaliby)) +- Dropped support for `networking/v1beta1` `Ingress` in `kubectl`. ([#135176](https://github.com/kubernetes/kubernetes/pull/135176), [@scaliby](https://github.com/scaliby)) +- Dropped support for `policy/v1beta1` PodDisruptionBudget in kubectl. ([#134685](https://github.com/kubernetes/kubernetes/pull/134685), [@scaliby](https://github.com/scaliby)) +- Eliminated and prevented future use of the `md5` algorithm in favor of more appropriate hashing algorithms. ([#133511](https://github.com/kubernetes/kubernetes/pull/133511), [@BenTheElder](https://github.com/BenTheElder)) [SIG Apps, Architecture, CLI, Cluster Lifecycle, Network, Node, Security, Storage and Testing] +- Fixed `nfacct` test cases on s390x. ([#133603](https://github.com/kubernetes/kubernetes/pull/133603), [@saisindhuri91](https://github.com/saisindhuri91)) +- Fixed formatting of various Go API deprecations for `GoDoc` and `pkgsite`, and enabled a linter to detect misformatted deprecations. ([#133571](https://github.com/kubernetes/kubernetes/pull/133571), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture, CLI, Instrumentation and Testing] +- Improved HPA performance when using container-specific resource metrics by optimizing container lookup logic to exit early once the target container is found, reducing unnecessary iterations through all containers in a pod. ([#133415](https://github.com/kubernetes/kubernetes/pull/133415), [@AadiDev005](https://github.com/AadiDev005)) [SIG Apps and Autoscaling] +- Increased the coverage to 89.8%. ([#132607](https://github.com/kubernetes/kubernetes/pull/132607), [@ylink-lfs](https://github.com/ylink-lfs)) +- Kube-apiserver: Fixed an issue where passing invalid `DeleteOptions` incorrectly returned a 500 status instead of 400. ([#133358](https://github.com/kubernetes/kubernetes/pull/133358), [@ostrain](https://github.com/ostrain)) +- Kubeadm: Updated the supported `etcd` version to `v3.5.23` for supported control plane versions `v1.31`, `v1.32`, and `v1.33`. ([#134692](https://github.com/kubernetes/kubernetes/pull/134692), [@joshjms](https://github.com/joshjms)) [SIG Cluster Lifecycle and Etcd] +- Kubeadm: stopped applying the `--pod-infra-container-image` flag for the kubelet. The flag has been deprecated and no longer served a purpose in the kubelet as the logic was migrated to CRI (Container Runtime Interface). During upgrade, kubeadm will attempt to remove the flag from the file `/var/lib/kubelet/kubeadm-flags.env`. ([#133778](https://github.com/kubernetes/kubernetes/pull/133778), [@carlory](https://github.com/carlory)) [SIG Cloud Provider and Cluster Lifecycle] +- Migrated the `CPUManager` to contextual logging. ([#125912](https://github.com/kubernetes/kubernetes/pull/125912), [@ffromani](https://github.com/ffromani)) +- Moved Types in `k/k/pkg/scheduler/framework`: + `Handle`, + `Plugin`, + `PreEnqueuePlugin`, `QueueSortPlugin`, `EnqueueExtensions`, `PreFilterExtensions`, `PreFilterPlugin`, `FilterPlugin`, `PostFilterPlugin`, `PreScorePlugin`, `ScorePlugin`, `ReservePlugin`, `PreBindPlugin`, `PostBindPlugin`, `PermitPlugin`, `BindPlugin`, + `PodActivator`, `PodNominator`, `PluginsRunner`, + `LessFunc`, `ScoreExtensions`, `NodeToStatusReader`, `NodeScoreList`, `NodeScore`, `NodePluginScores`, `PluginScore`, `NominatingMode`, `NominatingInfo`, `WaitingPod`, `PreFilterResult`, `PostFilterResult`, + `Extender`, + `NodeInfoLister`, `StorageInfoLister`, `SharedLister`, `ResourceSliceLister`, `DeviceClassLister`, `ResourceClaimTracker`, `SharedDRAManager` + + to package `k8s.io/kube-scheduler/framework`. Users should update import paths. The interfaces don't change. + + Type `Parallelizer` in `k/k/pkg/scheduler/framework/parallelism` has been split into interface `Parallelizer` (in `k8s.io/kube-scheduler/framework`) and `struct Parallelizer` (location unchanged in k/k). Plugin developers should update the import path to staging repo. ([#133172](https://github.com/kubernetes/kubernetes/pull/133172), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Release, Scheduling, Storage and Testing] +- Moved the CPU Manager static policy option `strict-cpu-reservation` to the GA version. ([#134388](https://github.com/kubernetes/kubernetes/pull/134388), [@psasnal](https://github.com/psasnal)) +- Promoted the Topology Manager policy option `max-allowable-numa-nodes` to GA version. ([#134614](https://github.com/kubernetes/kubernetes/pull/134614), [@ffromani](https://github.com/ffromani)) +- Reduced event spam during volume operation errors in the Portworx in-tree driver. ([#135081](https://github.com/kubernetes/kubernetes/pull/135081), [@gohilankit](https://github.com/gohilankit)) +- Removed `rsync` as a dependency to build Kubernetes. ([#134656](https://github.com/kubernetes/kubernetes/pull/134656), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release and Testing] +- Removed container name from messages for container created and started events. ([#134043](https://github.com/kubernetes/kubernetes/pull/134043), [@HirazawaUi](https://github.com/HirazawaUi)) +- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/dra` in favor of `google.golang.org/protobuf`. ([#133026](https://github.com/kubernetes/kubernetes/pull/133026), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery and Node] +- Removed general available feature-gate `SizeMemoryBackedVolumes`. ([#133720](https://github.com/kubernetes/kubernetes/pull/133720), [@carlory](https://github.com/carlory)) [SIG Node, Storage and Testing] +- Removed the `ComponentSLIs` feature gate, as it was promoted to stable in the Kubernetes `v1.32` release. ([#133742](https://github.com/kubernetes/kubernetes/pull/133742), [@carlory](https://github.com/carlory)) [SIG Architecture and Instrumentation] +- Removed the `KUBECTL_OPENAPIV3_PATCH` environment variable, as aggregated discovery has been stable since `v1.30`. ([#134130](https://github.com/kubernetes/kubernetes/pull/134130), [@ardaguclu](https://github.com/ardaguclu)) +- Removed the `UserNamespacesPodSecurityStandards` feature gate. The minimum supported Kubernetes version for `kubelet` is now `v1.31`, so the gate is no longer needed. ([#132157](https://github.com/kubernetes/kubernetes/pull/132157), [@haircommander](https://github.com/haircommander)) [SIG Auth, Node and Testing] +- Removed the `VolumeAttributesClass` resource from the `storage.k8s.io/v1alpha1` API in `v1.35`. ([#134625](https://github.com/kubernetes/kubernetes/pull/134625), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Etcd, Storage and Testing] +- Specified the deprecated version of `apiserver_storage_objects` metric in metrics docs. ([#134028](https://github.com/kubernetes/kubernetes/pull/134028), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Etcd and Instrumentation] +- Substantially simplified building Kubernetes by making the process run a pre-built container image directly without running `rsyncd`. ([#134510](https://github.com/kubernetes/kubernetes/pull/134510), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release and Testing] +- Tests: Switched to https://go.dev/doc/go1.25#container-aware-gomaxprocs from `go.uber.org/automaxprocs`. ([#133492](https://github.com/kubernetes/kubernetes/pull/133492), [@BenTheElder](https://github.com/BenTheElder)) +- The `AggregatedDiscoveryRemoveBetaType` feature gate was deprecated and locked to `true`. ([#134230](https://github.com/kubernetes/kubernetes/pull/134230), [@Jefftree](https://github.com/Jefftree)) +- The `SystemdWatchdog` feature gate has been locked to default and will be removed in future release. The systemd watchdog functionality in `kubelet` can be enabled via systemd without any feature gate configuration. See the [systemd watchdog documentation](https://kubernetes.io/docs/reference/node/systemd-watchdog/) for more information. ([#134691](https://github.com/kubernetes/kubernetes/pull/134691), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) +- Updated CNI plugins to v1.8.0. ([#133837](https://github.com/kubernetes/kubernetes/pull/133837), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider, Node and Testing] +- Updated `etcd` to `v3.6.5`. ([#134251](https://github.com/kubernetes/kubernetes/pull/134251), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- Updated `kubectl auth reconcile` to retry reconciliation when a conflict error occurs. ([#133323](https://github.com/kubernetes/kubernetes/pull/133323), [@liggitt](https://github.com/liggitt)) [SIG Auth and CLI] +- Updated `kubectl get` and `kubectl describe` human-readable output to no longer show counts for referenced tokens and secrets. ([#117160](https://github.com/kubernetes/kubernetes/pull/117160), [@liggitt](https://github.com/liggitt)) [SIG CLI and Testing] +- Updated cri-tools to v1.34.0. ([#133636](https://github.com/kubernetes/kubernetes/pull/133636), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider] +- Updated the Go version of Kubernetes to `1.25.3`. ([#134598](https://github.com/kubernetes/kubernetes/pull/134598), [@BenTheElder](https://github.com/BenTheElder)) +- Updated the `/statusz` page for `kube-proxy` to include a list of exposed endpoints, making debugging and introspection easier. ([#133190](https://github.com/kubernetes/kubernetes/pull/133190), [@aman4433](https://github.com/aman4433)) [SIG Network and Node] +- Updated the `kubectl wait` command description by removing the `Experimental` prefix, as the command has been stable for a long time. ([#133731](https://github.com/kubernetes/kubernetes/pull/133731), [@ardaguclu](https://github.com/ardaguclu)) +- Updated the etcd client library to `v3.6.5`. ([#134780](https://github.com/kubernetes/kubernetes/pull/134780), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] +- Updated the short description of the `kubectl wait` command by removing the `Experimental` prefix, as the command has been stable for a long time. ([#133907](https://github.com/kubernetes/kubernetes/pull/133907), [@ardaguclu](https://github.com/ardaguclu)) +- Upgraded CoreDNS to v1.12.4. ([#133968](https://github.com/kubernetes/kubernetes/pull/133968), [@yashsingh74](https://github.com/yashsingh74)) [SIG Cloud Provider and Cluster Lifecycle] +- Upgraded `CoreDNS` to `v1.12.3`. ([#132288](https://github.com/kubernetes/kubernetes/pull/132288), [@thevilledev](https://github.com/thevilledev)) [SIG Cloud Provider and Cluster Lifecycle] +- `kubeadm`: Removed the `WaitForAllControlPlaneComponents` feature gate, which graduated to GA in `v1.34` and was locked to enabled by default. ([#134781](https://github.com/kubernetes/kubernetes/pull/134781), [@neolit123](https://github.com/neolit123)) +- `kubeadm`: Updated the supported etcd version to `v3.5.24` for control plane versions `v1.32`, `v1.33`, and `v1.34`. ([#134779](https://github.com/kubernetes/kubernetes/pull/134779), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- `etcd: Update etcd to `v3.6.6`. (#135271, @bzsuni) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- Fix a bug in the kube-apiserver where a malformed Service without name can cause high CPU usage. The bug is present on the new Cluster IP allocators enabled with the feature MultiCIDRServiceAllocator (enabled by default since 1.33) + + +## Dependencies + +### Added +- cyphar.com/go-pathrs: v0.2.1 +- github.com/Masterminds/semver/v3: [v3.4.0](https://github.com/Masterminds/semver/tree/v3.4.0) +- github.com/gkampitakis/ciinfo: [v0.3.2](https://github.com/gkampitakis/ciinfo/tree/v0.3.2) +- github.com/gkampitakis/go-diff: [v1.3.2](https://github.com/gkampitakis/go-diff/tree/v1.3.2) +- github.com/gkampitakis/go-snaps: [v0.5.15](https://github.com/gkampitakis/go-snaps/tree/v0.5.15) +- github.com/goccy/go-yaml: [v1.18.0](https://github.com/goccy/go-yaml/tree/v1.18.0) +- github.com/joshdk/go-junit: [v1.0.0](https://github.com/joshdk/go-junit/tree/v1.0.0) +- github.com/maruel/natural: [v1.1.1](https://github.com/maruel/natural/tree/v1.1.1) +- github.com/mfridman/tparse: [v0.18.0](https://github.com/mfridman/tparse/tree/v0.18.0) +- github.com/moby/sys/atomicwriter: [v0.1.0](https://github.com/moby/sys/tree/atomicwriter/v0.1.0) +- github.com/tidwall/gjson: [v1.18.0](https://github.com/tidwall/gjson/tree/v1.18.0) +- github.com/tidwall/match: [v1.1.1](https://github.com/tidwall/match/tree/v1.1.1) +- github.com/tidwall/pretty: [v1.2.1](https://github.com/tidwall/pretty/tree/v1.2.1) +- github.com/tidwall/sjson: [v1.2.5](https://github.com/tidwall/sjson/tree/v1.2.5) +- go.uber.org/automaxprocs: v1.6.0 +- golang.org/x/tools/go/expect: v0.1.1-deprecated +- golang.org/x/tools/go/packages/packagestest: v0.1.1-deprecated + +### Changed +- cloud.google.com/go/compute/metadata: v0.6.0 → v0.7.0 +- github.com/aws/aws-sdk-go-v2/config: [v1.27.24 → v1.29.14](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.24...config/v1.29.14) +- github.com/aws/aws-sdk-go-v2/credentials: [v1.17.24 → v1.17.67](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.17.24...credentials/v1.17.67) +- github.com/aws/aws-sdk-go-v2/feature/ec2/imds: [v1.16.9 → v1.16.30](https://github.com/aws/aws-sdk-go-v2/compare/feature/ec2/imds/v1.16.9...feature/ec2/imds/v1.16.30) +- github.com/aws/aws-sdk-go-v2/internal/configsources: [v1.3.13 → v1.3.34](https://github.com/aws/aws-sdk-go-v2/compare/internal/configsources/v1.3.13...internal/configsources/v1.3.34) +- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2: [v2.6.13 → v2.6.34](https://github.com/aws/aws-sdk-go-v2/compare/internal/endpoints/v2/v2.6.13...internal/endpoints/v2/v2.6.34) +- github.com/aws/aws-sdk-go-v2/internal/ini: [v1.8.0 → v1.8.3](https://github.com/aws/aws-sdk-go-v2/compare/internal/ini/v1.8.0...internal/ini/v1.8.3) +- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding: [v1.11.3 → v1.12.3](https://github.com/aws/aws-sdk-go-v2/compare/service/internal/accept-encoding/v1.11.3...service/internal/accept-encoding/v1.12.3) +- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url: [v1.11.15 → v1.12.15](https://github.com/aws/aws-sdk-go-v2/compare/service/internal/presigned-url/v1.11.15...service/internal/presigned-url/v1.12.15) +- github.com/aws/aws-sdk-go-v2/service/sso: [v1.22.1 → v1.25.3](https://github.com/aws/aws-sdk-go-v2/compare/service/sso/v1.22.1...service/sso/v1.25.3) +- github.com/aws/aws-sdk-go-v2/service/ssooidc: [v1.26.2 → v1.30.1](https://github.com/aws/aws-sdk-go-v2/compare/service/ssooidc/v1.26.2...service/ssooidc/v1.30.1) +- github.com/aws/aws-sdk-go-v2/service/sts: [v1.30.1 → v1.33.19](https://github.com/aws/aws-sdk-go-v2/compare/service/sts/v1.30.1...service/sts/v1.33.19) +- github.com/aws/aws-sdk-go-v2: [v1.30.1 → v1.36.3](https://github.com/aws/aws-sdk-go-v2/compare/v1.30.1...v1.36.3) +- github.com/aws/smithy-go: [v1.20.3 → v1.22.3](https://github.com/aws/smithy-go/compare/v1.20.3...v1.22.3) +- github.com/containerd/containerd/api: [v1.8.0 → v1.9.0](https://github.com/containerd/containerd/compare/api/v1.8.0...api/v1.9.0) +- github.com/containerd/ttrpc: [v1.2.6 → v1.2.7](https://github.com/containerd/ttrpc/compare/v1.2.6...v1.2.7) +- github.com/containerd/typeurl/v2: [v2.2.2 → v2.2.3](https://github.com/containerd/typeurl/compare/v2.2.2...v2.2.3) +- github.com/coredns/corefile-migration: [v1.0.26 → v1.0.29](https://github.com/coredns/corefile-migration/compare/v1.0.26...v1.0.29) +- github.com/cyphar/filepath-securejoin: [v0.4.1 → v0.6.0](https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...v0.6.0) +- github.com/docker/docker: [v26.1.4+incompatible → v28.2.2+incompatible](https://github.com/docker/docker/compare/v26.1.4...v28.2.2) +- github.com/go-logr/logr: [v1.4.2 → v1.4.3](https://github.com/go-logr/logr/compare/v1.4.2...v1.4.3) +- github.com/google/cadvisor: [v0.52.1 → v0.53.0](https://github.com/google/cadvisor/compare/v0.52.1...v0.53.0) +- github.com/google/pprof: [d1b30fe → 27863c8](https://github.com/google/pprof/compare/d1b30fe...27863c8) +- github.com/onsi/ginkgo/v2: [v2.21.0 → v2.27.2](https://github.com/onsi/ginkgo/compare/v2.21.0...v2.27.2) +- github.com/onsi/gomega: [v1.35.1 → v1.38.2](https://github.com/onsi/gomega/compare/v1.35.1...v1.38.2) +- github.com/opencontainers/cgroups: [v0.0.1 → v0.0.3](https://github.com/opencontainers/cgroups/compare/v0.0.1...v0.0.3) +- github.com/opencontainers/runc: [v1.2.5 → v1.3.0](https://github.com/opencontainers/runc/compare/v1.2.5...v1.3.0) +- github.com/opencontainers/runtime-spec: [v1.2.0 → v1.2.1](https://github.com/opencontainers/runtime-spec/compare/v1.2.0...v1.2.1) +- github.com/opencontainers/selinux: [v1.11.1 → v1.13.0](https://github.com/opencontainers/selinux/compare/v1.11.1...v1.13.0) +- github.com/prometheus/client_golang: [v1.22.0 → v1.23.2](https://github.com/prometheus/client_golang/compare/v1.22.0...v1.23.2) +- github.com/prometheus/client_model: [v0.6.1 → v0.6.2](https://github.com/prometheus/client_model/compare/v0.6.1...v0.6.2) +- github.com/prometheus/common: [v0.62.0 → v0.66.1](https://github.com/prometheus/common/compare/v0.62.0...v0.66.1) +- github.com/prometheus/procfs: [v0.15.1 → v0.16.1](https://github.com/prometheus/procfs/compare/v0.15.1...v0.16.1) +- github.com/rogpeppe/go-internal: [v1.13.1 → v1.14.1](https://github.com/rogpeppe/go-internal/compare/v1.13.1...v1.14.1) +- github.com/spf13/cobra: [v1.9.1 → v1.10.0](https://github.com/spf13/cobra/compare/v1.9.1...v1.10.0) +- github.com/spf13/pflag: [v1.0.6 → v1.0.9](https://github.com/spf13/pflag/compare/v1.0.6...v1.0.9) +- github.com/stretchr/testify: [v1.10.0 → v1.11.1](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1) +- go.etcd.io/bbolt: v1.4.2 → v1.4.3 +- go.etcd.io/etcd/api/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/client/pkg/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/client/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/pkg/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/server/v3: v3.6.4 → v3.6.5 +- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp: v0.58.0 → v0.61.0 +- go.opentelemetry.io/otel/metric: v1.35.0 → v1.36.0 +- go.opentelemetry.io/otel/sdk/metric: v1.34.0 → v1.36.0 +- go.opentelemetry.io/otel/sdk: v1.34.0 → v1.36.0 +- go.opentelemetry.io/otel/trace: v1.35.0 → v1.36.0 +- go.opentelemetry.io/otel: v1.35.0 → v1.36.0 +- go.yaml.in/yaml/v2: v2.4.2 → v2.4.3 +- golang.org/x/crypto: v0.36.0 → v0.45.0 +- golang.org/x/mod: v0.21.0 → v0.29.0 +- golang.org/x/net: v0.38.0 → v0.47.0 +- golang.org/x/oauth2: v0.27.0 → v0.30.0 +- golang.org/x/sync: v0.12.0 → v0.18.0 +- golang.org/x/sys: v0.31.0 → v0.38.0 +- golang.org/x/telemetry: bda5523 → 078029d +- golang.org/x/term: v0.30.0 → v0.37.0 +- golang.org/x/text: v0.23.0 → v0.31.0 +- golang.org/x/tools: v0.26.0 → v0.38.0 +- google.golang.org/genproto/googleapis/rpc: a0af3ef → 200df99 +- google.golang.org/grpc: v1.72.1 → v1.72.2 +- google.golang.org/protobuf: v1.36.5 → v1.36.8 +- gopkg.in/evanphx/json-patch.v4: v4.12.0 → v4.13.0 +- k8s.io/gengo/v2: 85fd79d → ec3ebc5 +- k8s.io/kube-openapi: f3f2b99 → 589584f +- k8s.io/system-validators: v1.10.1 → v1.12.1 +- k8s.io/utils: 4c0f3b2 → bc988d5 +- sigs.k8s.io/json: cfa47c3 → 2d32026 + +### Removed +- gopkg.in/yaml.v2: v2.4.0 + + + +# v1.35.0-rc.1 + + +## Downloads for v1.35.0-rc.1 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes.tar.gz) | 6b08badb1402c5a4d5e83e14bc64e464c7bd8bbdd9473ea1b501b7bf04ac9f53d2ac23a5f70761cc03024bd046c329253d2914af9225530755bcbc06d7459616 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-src.tar.gz) | 35d2827a2bb7b01162c506d7a15392c72c6537f9f1570ade160bc286ad9a409e0830d32e7ea5ae8191bb6435859826d2d5ec56255a29fe279aee3517239cb9a6 + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-darwin-amd64.tar.gz) | 85f9f154296b0579444ee9ff43f74ad616ef52e453782da8dd10f5150d1fb6f1d71151b7525fe5784142f930fb9fe9bfbc395277b5a088d3bc892c9415e15611 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-darwin-arm64.tar.gz) | 2811a1b3901c9a82cb042e4b4c4bc4d75ea0d894b42e7f7c63b25b5df8d26b5ff2bfd0ee819b9cb8e946a2bac90ac4dfb649c692f054fb290e08516966384d0b +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-386.tar.gz) | aed99699ba9f635d1e073061ab89b7ee80744cb56a4aa078ad6afc5670483db456fcc6a544bf34b95a4a3c8f25938898b093d7d279120063688f0406efaf37f9 +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-amd64.tar.gz) | 607724204e2b3265f25d12cda5397c4943e8df4ae66efe2f5cf2582b7aa1c9fc9a60c61a193aef8ae1ee5b3d261146f5cc9956de873d82864979ef19c70c48c7 +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-arm.tar.gz) | 378770b5529ce39fbdbb1d39c4a966ad96c03523411e5f830ac2e86e67e23518de534f148838903561de5a7db6f852f532ad7a0799514b72892fea0362f3c635 +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-arm64.tar.gz) | 357f66e108d651d0a77d61f9f0da286f5c8d312d6f320174dab42cd6aae69ecdf24e081f5bdcbc6313d2688c2585009348766f7a0eed58f23f8eda898d3e7081 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-ppc64le.tar.gz) | ac9d343138f01a1d6fa986ef684938686e7ac656f8b8b1b5c1a75894e2c98f7f36754eef15a95b20c0812c5a62d775a285c4e015b5eca9bc4e937b9f44b7a537 +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-linux-s390x.tar.gz) | b12631a0bb8c2f9e28ca855302a57ab4bffb2db4105668cf16635193cada26bd0aa63f80428835339f42eea13839d1990f171276697eb00f9cbe5d8edae434f0 +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-windows-386.tar.gz) | 5b1849523011be7f569f17f42250a98a2513caa7652c3055712bace9ba440ab4e38d4f5bceb6e89158f4f3008a836fcee765ef9af818ba6cb07296ef8467cf02 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-windows-amd64.tar.gz) | fb95a54326565bcc3458fa0a876d5f457e5218eafa349f9cee3db93982c30482b8fe57d0f43a2be89e4816382a5efc97b95fae069bc2ed8f19b6ac253e46790a +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-client-windows-arm64.tar.gz) | 465a7fe0e87dd3dc0b22d3881bbe4c33e706ca53a512a981afd49bccdd5d638a818cabb84dbc5796b54ce97d045bbaf466c0cbf46f809bb5a31d64117386a0c7 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-server-linux-amd64.tar.gz) | c6728d534c85e11a58dea5ca830541efde07f390586ca77f7f59308179b677f3f3f28492e1c26314adf79e800b7690a1b0d5ac5670d18282064bc4fbb4093b05 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-server-linux-arm64.tar.gz) | 6b9eb6d36179bef49b8f4f276f1ffe03552876e46c9477129b6e9a690c38e7343e0430e22fbbfaccab83c3879282e6994cdaca47a709267617a84e3143b4598e +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-server-linux-ppc64le.tar.gz) | 946b9b5d817437258a77e6b3fcf674c0c9adb0a5b4cc2aa3f68f8364c7916016d6811a9596221801f570018f3976ae456eb01a6ad871fb5461daebfd1c2ff589 +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-server-linux-s390x.tar.gz) | 5d5c22a1149d5c741b5c37710a97a84b2f9f45ccc9b8457408233a6a7c5fc20c3a2bbf92b68f135630308991cdc463559cdad6131fa6c9e0ad5cfdca7933cdc2 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-node-linux-amd64.tar.gz) | 269725e2afd028e6eb8a3d12605789f6a5715a0bf9cf477df689bdc9f9b164f0fd32c3007b16ffee17c5234f6fff17ff9096396892e71155074a3499e9480088 +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-node-linux-arm64.tar.gz) | 37472ba5b63177bcb9430b761f1d4df459771cab2bf9448f69fa5abe0dc4f2e0a26bd6b9c8d747d37e05e8e0263736ca4150a624cecf03938aacd4e98929d03d +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-node-linux-ppc64le.tar.gz) | 25dce33f916a55d27f825902fc7776735a5ddfd0e86555b193d431eab54781d52fff9c41dafcf5a845e79f0153d515b014cb8a5c21179c82e96321851109195c +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-node-linux-s390x.tar.gz) | 16e22350e2871ea0d42668bb9c1487b2c5a6e6b436c78d1f986f68bc0312d340ab0285aca7f619322e3238cf9ee90f5425e49dc018e53069e5b0cd092bdd28a2 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.1/kubernetes-node-windows-amd64.tar.gz) | 1b4aab8eb20bd974c7585ba55a5caeb43174afbef738e1bb99fd51fd228b800c943c4a8f0204ef935c733b15cab52ae0ae2a9a7da9299387be272932b1c54c9e + +### 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.35.0-rc.1](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.35.0-rc.1](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.35.0-rc.1](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.35.0-rc.1](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.35.0-rc.1](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.35.0-rc.1](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.35.0-rc.0 + +## Changes by Kind + +### Feature + +- Kubernetes is now built using Go 1.25.5 ([#135609](https://github.com/kubernetes/kubernetes/pull/135609), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] + +### Bug or Regression + +- Fix a bug in the kube-apiserver where a malformed Service without name can cause high CPU usage. The bug is present on the new Cluster IP allocators enabled with the feature MultiCIDRServiceAllocator (enabled by default since 1.33) ([#135499](https://github.com/kubernetes/kubernetes/pull/135499), [@aojea](https://github.com/aojea)) [SIG Testing] +- Fixes a bug where MutatingAdmissionPolicy would fail to apply to objects with duplicate list items (like env vars). ([#135560](https://github.com/kubernetes/kubernetes/pull/135560), [@lalitc375](https://github.com/lalitc375)) [SIG API Machinery] +- K8s.io/client-go: Fixes a regression in 1.34+ which prevented informers from using configured Transformer functions ([#135580](https://github.com/kubernetes/kubernetes/pull/135580), [@serathius](https://github.com/serathius)) [SIG API Machinery] + +### Other (Cleanup or Flake) + +- Etcd: Update etcd to v3.6.6 ([#135271](https://github.com/kubernetes/kubernetes/pull/135271), [@bzsuni](https://github.com/bzsuni)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] + +## Dependencies + +### Added +_Nothing has changed._ + +### Changed +- golang.org/x/crypto: v0.41.0 → v0.45.0 +- golang.org/x/mod: v0.28.0 → v0.29.0 +- golang.org/x/net: v0.43.0 → v0.47.0 +- golang.org/x/sync: v0.17.0 → v0.18.0 +- golang.org/x/sys: v0.37.0 → v0.38.0 +- golang.org/x/telemetry: 1a19826 → 078029d +- golang.org/x/term: v0.36.0 → v0.37.0 +- golang.org/x/text: v0.29.0 → v0.31.0 +- golang.org/x/tools: v0.36.0 → v0.38.0 + +### Removed +_Nothing has changed._ + + + +# v1.35.0-rc.0 + + +## Downloads for v1.35.0-rc.0 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes.tar.gz) | ef5c549823abd93a260ef90bd282362015e8e7e4f079bb59acc3104191bed88eed98638726866ef9d5e56849cbe58b641e7ebaf0d247c0088deaae81141b4007 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-src.tar.gz) | 906dbf942d446b9ffaa56dc31024dd496c609599a5391c81891ed2223c60017eec879de2d68d8ac5003e05c03cdaf5584186b3303291ce81885ec1875e24748c + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-darwin-amd64.tar.gz) | f771e62019fc1cba671015aca0efaf24492598d8200330672e61525a0cd56d406ce53bde56ed1bbdfac4b1ac09e8b4820ce9d612904cf9169f075010a81f3548 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-darwin-arm64.tar.gz) | 74c1b281351df048797db693cb0fe01dc4646d7884b1588e80184fb41ef83bb6016d095e0266eb7993fc2e75d54df6bcf4b8909f6c1e0a7245afb457a6f17516 +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-386.tar.gz) | ca6b1747442056c10cbc838ffaca3ace45d7fc773183d526aaec728bb322370f2cc05b2b7304dc19434b40d1fb3b39f7e4eb1930cc9952ac27abccf260968737 +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-amd64.tar.gz) | c9e5e3aa9df4e8c2f1253e4f72cdea94a8deae601b5df0b2cbdac4babdd2d2ac85191c908453bca40a2a0b44247dbb18ef1b5a7751c16660f9487391ed8dacad +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-arm.tar.gz) | 167490337b040849d953de5aa32e3c5491f4d6225fc0c5c86ff6ef9820f61e189f91da26831ee2d910749b38331ec1a73ff820bc29497939935e1f1b3462bf9a +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-arm64.tar.gz) | 21d7091f25a112e6f940bc1a047294a2c6cd03a8b52c3cfb7ac024460d21ad5ab0a5aea448ec705ed3c5f0619d512a132051eea492e7d7ae008716abbab51a24 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-ppc64le.tar.gz) | b4a1b9c03f7de8e0e2c89b1c32fcd4347a287eb62fe21bc8b4d6abafc3ae30559173bd070b5ddcb94038bc7d980e09d946a131ef0191ce2febbadffd1621143e +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-linux-s390x.tar.gz) | 737916148885ade333cbd5c7f927d32e30bb6f6bfe78db8c3c00c05c82d0a6ea5255a6c85b256acb14dc2b322e79e0af6bfa4f2ee055625da0b7ba29f99c847b +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-windows-386.tar.gz) | aa6b19469d3268235012a4a6a2f3d6ee3d56734b083133c213fa7064afef6f3caecadf9a714b6905ba69c22f0103eb44135e51a00907e4ddd1856c14b988ea58 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-windows-amd64.tar.gz) | 7d7c7edc9e67fecdb0d2a96992933acf2e4d68680ea20349af3feabf449072130de99a9aad6e03ca47806c1e10415de4fb69e56e8f73293fde96eb27bf02a71b +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-client-windows-arm64.tar.gz) | c5302ca43bbf8ba1f2f4d89f561edb53c4a6021d084593524a20418dff20c6f4f0052ad85a230961b208a40ca89d4daa64852e9730ce2350932e4d9e369f1621 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-server-linux-amd64.tar.gz) | 5aa98c85cefbdc12c7816add8c5eb40438e39779fc96cd0606b21ab89c4d0af354168572f39787cc3f3ef471862d543b1804d433f76f52191490498aa4670255 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-server-linux-arm64.tar.gz) | 0942d2aee9638b32a976dc623269332d1f82feb0dc601e009fe97a8179cd23704fdd3ccecb3ecb51cea1abf57bee89d60707b1e69679580e1534ab4222820b01 +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-server-linux-ppc64le.tar.gz) | 76a9351112c6f40752f3c2526bcc3fbb100f7e577ca7746644087c386f4e38f01f12b6306a329ec3bcbfc33ce244db95fd3035cc2c37dc32920181f26b7cf78b +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-server-linux-s390x.tar.gz) | bb70ad37cd30db82afa9dbd2b6a221f7ed43d6ec54c8a22ff852c5ac5364f2abc7c30db26640d42ba3e265dff70607f5dde86075fe841fe13500c811000ba0bc + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-node-linux-amd64.tar.gz) | dcb41e49aed2c0c686c283a721a509d1d72f8cda4ad15216cdf6f65b48b7ba803a88a22aa2dc2bb35c4ffcc329f9184638423e3b0e0e5edd0142ed05922617d1 +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-node-linux-arm64.tar.gz) | e91bcc56eea9d07aa65b2e855d9b15c6b480c05bc9581cd3aed740d3fe712b064cd9a9dd1baea27e0678747179bd76752ccdf510cc4855764f38b78cf2d9ebdf +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-node-linux-ppc64le.tar.gz) | 7870ee543c2a131451eaaa7b62e7292045e5ee0d4cd592c385a81170d1787835883d58326adabe201df9f920d2bedc8a1c59714b541dabe4f8ee4b9491bbdd23 +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-node-linux-s390x.tar.gz) | d8265447f63f2466b0d890b3f37918b70cadec0a3e0682f5410414d5456403e992dc859129447b02980b544ddb43cb6feef421d95d29ec5c2bd3fba8811884ca +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-rc.0/kubernetes-node-windows-amd64.tar.gz) | 8cbb1c7fd68dc474fbb88baaf4f169d9adf3ee177a963be3927c51bc08ab19ca5f1cb6a20fc45b52eba8c95b8abdf884d9f140dfacf4b64bfc5f3bf3540cd481 + +### 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.35.0-rc.0](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.35.0-rc.0](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.35.0-rc.0](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.35.0-rc.0](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.35.0-rc.0](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.35.0-rc.0](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.35.0-beta.0 + +## Changes by Kind + +### Feature + +- Kubernetes is now built using Go 1.25.4 ([#135492](https://github.com/kubernetes/kubernetes/pull/135492), [@cpanato](https://github.com/cpanato)) [SIG Release and Testing] + +### Bug or Regression + +- Fixes a spurious "namespace not found" error possible in default configurations in 1.30+ when using ValidatingAdmissionPolicy or MutatingAdmissionPolicy to intercept namespaced objects in newly-created namespaces ([#135359](https://github.com/kubernetes/kubernetes/pull/135359), [@liggitt](https://github.com/liggitt)) [SIG API Machinery] +- Kube-apiserver: Fixes spurious warning log messages about enabled alpha APIs while starting API server ([#135327](https://github.com/kubernetes/kubernetes/pull/135327), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery] + +## Dependencies + +### Added +_Nothing has changed._ + +### Changed +_Nothing has changed._ + +### Removed +_Nothing has changed._ + + + +# v1.35.0-beta.0 + + +## Downloads for v1.35.0-beta.0 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes.tar.gz) | 17fae05597b73bf8ed2c14bfbc7d863e6ca470877be12a510cb354bcaf4fa5f9b15b3702e45d231efe9f4865f687bf8d1ace312b4e0a15442a14c9997f1caa07 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-src.tar.gz) | a51fcd8dbe8097f1890931435bdeaf9c1aa31ae3c55ae6abeb504aa881c3e125ecb72af7518a9d3d38ffe67fbcffc6f1dd9e1e456218856ba2f25ec4d466f339 + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-darwin-amd64.tar.gz) | 50e6712a9d2a35d782ac0ddb22eb0799fdceca6c434c5ebe446e9f49bf9b7612cd3af3f31af211d8364d128d8b75f87ba9e61466aa7c6552416c50c14b08dd78 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-darwin-arm64.tar.gz) | c181381e2554d20b5ffbe024b33b8593800491bccceb98eaacc25fbc9ef44be4c360ec59603b857b730670c6cdcfe8d8428b790e1413d402f699c6579877ed4a +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-386.tar.gz) | 26fcb99525560328c9ab1e856741e3eb6aecbb3fc8e9adf72daaeb0f6c57058e61989f696ad866a0f9b1a43098914933bed142077754e0bd82bd2054965fe44a +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-amd64.tar.gz) | d6118e683ea4a64b1812a0eb0374678879a0b0b37868bd8b43517e5961a46b36f9addfbdbd84aa87e6dd4510afa644102140ec727bef3143788e2c97f23cc318 +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-arm.tar.gz) | cafd385adecb9ed43201df1c5206f127177fbef0345972a5492c62eb4304c8666eafc1bffa9fb54f530d4258d8f7aaded83f08efaab5391bf92ac50a4d53122f +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-arm64.tar.gz) | bb7d2281b2b9f02ae61a9607d932873a2dfd1ed551c79209a55feba88219093c8594c03c3915e87f7d847c6e59cf75625123dce8f367c34bb16d4e0ad5681f22 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-ppc64le.tar.gz) | 198e2102eb0b24e6c6b406ceb8cce6803154c3e68bef5ace7583683cd49d09b6a8ecbd2ca8f1d105a6454b8550b1d6132e65e618dd0ac49c9d36494af24e3ecb +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-linux-s390x.tar.gz) | 6e81675b8b523aa1df9f4599548f92bc990a2dbe6ab4f6a4039477e237f2e6286a19e2786ba436bada30969cd5b2f3fd2fb8bc16a41943fcf6865f8b6b690f67 +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-windows-386.tar.gz) | 0a68cd18169b4f269766aaff195b07fef44418f6e108ce47f6ce445407a28c1ff27cde4ef015bb01affd2309044a5ef86392132112681edfd5fe2e1632e99862 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-windows-amd64.tar.gz) | d64bba50e7878fb1bc89dd31e9c4a2796364d2226fcfb14964ca2783d18fe482824b6cd0a4b842076998db1794c6a5e33d78fd3bd2d5ae696ab438d4c7207114 +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-client-windows-arm64.tar.gz) | c1820e5be65918d6278ed8897059c5d87d8faa8640a5a7bab7f28822d2af19f1dbba650069a469b8eb955353d9c47a7f3098e9c7598f7f1272d08e4edba5bff8 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-server-linux-amd64.tar.gz) | f57c7fb934e0261f71fa7f2e219730cc977367bd015a0572d4446f28c9a70e89f641d029d206d27238c7fa27ba166a6c1e81e129b583e8c1555513d5360fabf0 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-server-linux-arm64.tar.gz) | 1d5b399f921da76ba0f88c9c28a64880a7be32c017fefd961939c3538e0361cd08f1d7bb38b09982600c5d09711a89c13ee757363d3f10101ee0b1775c85f97e +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-server-linux-ppc64le.tar.gz) | 1b6dbe8765ad8f740e699b842447d513b3b4e0a685db8380a3f38fc89efd3b962b0ec3026e6df55ac4c3cadc0120577d34e4cf5c10311fd1d2bcde9bc47ba844 +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-server-linux-s390x.tar.gz) | dd0213e41f26158f3cb9a589ca68211d4d68ca2cc9346726361f609b896b4b8975274dd53d3e7561c1f61178bd9aef69c156a435b530def48c215e2d03d18414 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-node-linux-amd64.tar.gz) | 7c43a88e1b86871d5f76d3d3fed4c458aedcb7d41f3dd944f08a24087b6e0703b2ce8c4d34ee2625de18d75b5bfbc36f5ab6da5158e69ba929cd1cf9f0a205cc +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-node-linux-arm64.tar.gz) | 0fbca961eead65de9401ff6211792f27f845003bb5d91655299b3d0c05dc805eca0ade4e0caf784d561734e1ba15ac1112efd92a439953835730a866c92f2205 +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-node-linux-ppc64le.tar.gz) | c9bbfcb37f32d267e00067b7334b5a961e875be5776a941ef84a2b2a9d8ee7f2bd97998a789b2180809a9869dbbfa52ef3b5c154447207a83c1de4fa07ed7b05 +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-node-linux-s390x.tar.gz) | 32b416cc48008e5c1b34af1205a880f8c43a194c264451222bb6281ffa894e4267d2ebdd49e4b08731a44274f5bd7c7ff54733dfd6aaa5df89ac57c12bc50073 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-beta.0/kubernetes-node-windows-amd64.tar.gz) | 90eb6f5b268eacadeba1be60f2765740c77a65b3f2b357cec8814a225c26bb8a60341b1c515c0867abf119f3522c7d90281a5490ed7c8926c2ecb7c8ec0b4fe9 + +### 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.35.0-beta.0](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.35.0-beta.0](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.35.0-beta.0](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.35.0-beta.0](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.35.0-beta.0](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.35.0-beta.0](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.35.0-alpha.3 + +## Changes by Kind + +### API Change + +- Add scoring for the prioritized list feature so that the node that can satisfy the best ranked subrequests are chosen. ([#134711](https://github.com/kubernetes/kubernetes/pull/134711), [@mortent](https://github.com/mortent)) [SIG Node, Scheduling and Testing] +- Allows restart all containers when the source container exits with a matching restart policy rule. This is an alpha feature behind feature gate RestartAllContainersOnContainerExit. ([#134345](https://github.com/kubernetes/kubernetes/pull/134345), [@yuanwang04](https://github.com/yuanwang04)) [SIG Apps, Node and Testing] +- Changed kuberc configuration schema. Two new optional fields added to kuberc configuration, `credPluginPolicy` and `credPluginAllowlist`. This is documented in [KEP-3104](https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/3104-introduce-kuberc/README.md#allowlist-design-details) and documentation is added to the website by [kubernetes/website#52877](https://github.com/kubernetes/website/pull/52877) ([#134870](https://github.com/kubernetes/kubernetes/pull/134870), [@pmengelbert](https://github.com/pmengelbert)) [SIG API Machinery, Architecture, Auth, CLI, Instrumentation and Testing] +- Enhanced discovery response to support merged API groups/resources from all peer apiservers when UnknownVersionInteroperabilityProxy feature is enabled ([#133648](https://github.com/kubernetes/kubernetes/pull/133648), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Auth, Cloud Provider, Node, Scheduling and Testing] +- Extend `core/v1 Toleration` to support numeric comparison operators (`Gt`, `Lt`). ([#134665](https://github.com/kubernetes/kubernetes/pull/134665), [@helayoty](https://github.com/helayoty)) [SIG API Machinery, Apps, Node, Scheduling, Testing and Windows] +- Features: NominatedNodeNameForExpectation in kube-scheduler and CleaeringNominatedNodeNameAfterBinding in kube-apiserver are now enabled by default. ([#135103](https://github.com/kubernetes/kubernetes/pull/135103), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- Implement changes to prevent pod scheduling to a node without CSI driver ([#135012](https://github.com/kubernetes/kubernetes/pull/135012), [@gnufied](https://github.com/gnufied)) [SIG API Machinery, Scheduling, Storage and Testing] +- Introduce scheduling.k8s.io/v1alpha1 Workload API to allow for expressing workload-level scheduling requirements and let kube-scheduler act on those. ([#134564](https://github.com/kubernetes/kubernetes/pull/134564), [@macsko](https://github.com/macsko)) [SIG API Machinery, Apps, CLI, Etcd, Scheduling and Testing] +- Introduce the alpha MutableSchedulingDirectivesForSuspendedJobs feature gate (disabled by default) which: + 1. allows to mutate Job's scheduling directives for suspended Jobs + 2. makes the Job controller to clear the status.startTime field for suspended Jobs ([#135104](https://github.com/kubernetes/kubernetes/pull/135104), [@mimowo](https://github.com/mimowo)) [SIG Apps and Testing] +- Introduced GangScheduling kube-scheduler plugin to enable "all-or-nothing" scheduling. Workload API in scheduling.k8s.io/v1alpha1 is used to express the desired policy. ([#134722](https://github.com/kubernetes/kubernetes/pull/134722), [@macsko](https://github.com/macsko)) [SIG API Machinery, Apps, Auth, CLI, Etcd, Scheduling and Testing] +- PV node affinity is now mutable. ([#134339](https://github.com/kubernetes/kubernetes/pull/134339), [@huww98](https://github.com/huww98)) [SIG API Machinery, Apps and Node] +- ResourceQuota now counts device class requests within a ResourceClaim object as consuming two additional quotas when the DRAExtendedResource feature is enabled: + - `requests.deviceclass.resource.k8s.io/` with a quantity equal to the worst case count of devices requested + - requests for device classes that map to an extended resource consume `requests.` ([#134210](https://github.com/kubernetes/kubernetes/pull/134210), [@yliaog](https://github.com/yliaog)) [SIG API Machinery, Apps, Node, Scheduling and Testing] +- The DRA device taints and toleration feature now has a separate feature gate, DRADeviceTaintRules, which controls whether support for DeviceTaintRules is enabled. It is possible to disable that and keep DRADeviceTaints enabled, in which case tainting by DRA drivers through ResourceSlices continues to work. ([#135068](https://github.com/kubernetes/kubernetes/pull/135068), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Node, Scheduling and Testing] +- The ImagePullIntent and ImagePulledRecord objects used by kubelet to store information about image pulls have been moved to the v1beta1 API version. ([#132579](https://github.com/kubernetes/kubernetes/pull/132579), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- The KubeletEnsureSecretPulledImages feature is now beta and enabled by default. ([#135228](https://github.com/kubernetes/kubernetes/pull/135228), [@aramase](https://github.com/aramase)) [SIG Auth, Node and Testing] +- This change adds a new alpha feature Node Declared Features, which includes: + - A new `Node.Status.DeclaredFeatures` field for Kubelet to publish node-specific features. + - A library in `component-helpers` for feature registration and inference. + - A scheduler plugin (`NodeDeclaredFeatures`) scheduler plugin to match pods with nodes that provide their required features. + - An admission plugin (`NodeDeclaredFeatureValidator`) to validate pod updates against a node's declared features. ([#133389](https://github.com/kubernetes/kubernetes/pull/133389), [@pravk03](https://github.com/pravk03)) [SIG API Machinery, Apps, Node, Release, Scheduling and Testing] +- This change allows In Place Resize of Pod Level Resources + - Add Resources in PodStatus to capture resources set at pod-level cgroup + - Add AllocatedResources in PodStatus to capture resources requested in the PodSpec ([#132919](https://github.com/kubernetes/kubernetes/pull/132919), [@ndixita](https://github.com/ndixita)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Instrumentation, Node, Scheduling and Testing] +- Updates to the Partitionable Devices feature which allows for referencing counter sets across different ResourceSlices within the same resource pool. + + Devices from incomplete pools are no longer considered for allocation. + + This contains backwards incompatible changes to the Partitionable Devices alpha feature, so any ResourceSlices that uses the feature should be removed prior to upgrading or downgrading between 1.34 and 1.35. ([#134189](https://github.com/kubernetes/kubernetes/pull/134189), [@mortent](https://github.com/mortent)) [SIG API Machinery, Node, Scheduling and Testing] + +### Feature + +- Add cloud-controller-manager feature gate CloudControllerManagerWatchBasedRoutesReconciliation ([#131220](https://github.com/kubernetes/kubernetes/pull/131220), [@lukasmetzner](https://github.com/lukasmetzner)) [SIG API Machinery and Cloud Provider] +- Add the `UserNamespacesHostNetworkSupport` feature gate. The feature gate defaults to disabled. When the feature gate is enabled, will allow `hostNetwork` pods to use `user namespace`. ([#134893](https://github.com/kubernetes/kubernetes/pull/134893), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Apps, Node and Testing] +- Added a new `source` label in `resourceclaim_controller_resource_claims`. + Added a new metrics for DRAExtendedResource `scheduler_resourceclaim_creates_total`. ([#134523](https://github.com/kubernetes/kubernetes/pull/134523), [@bitoku](https://github.com/bitoku)) [SIG Apps, Instrumentation, Node and Scheduling] +- Added configurable per-device health check timeouts to the DRA health monitoring API. ([#135147](https://github.com/kubernetes/kubernetes/pull/135147), [@harche](https://github.com/harche)) [SIG Node] +- Bump ImageGCMaximumAge to stable ([#134736](https://github.com/kubernetes/kubernetes/pull/134736), [@haircommander](https://github.com/haircommander)) [SIG Node and Testing] +- Enables the `WatchListClient` feature gate. ([#134180](https://github.com/kubernetes/kubernetes/pull/134180), [@p0lyn0mial](https://github.com/p0lyn0mial)) [SIG API Machinery, Apps, Auth, CLI, Instrumentation, Node and Testing] +- Graduate PodTopologyLabelsAdmission feature gate to Beta and on by default. + + Pods will now have labels `topology.kubernetes.io/zone` and `topology.kubernetes.io/region` by default if the assigned Node has these labels. ([#135158](https://github.com/kubernetes/kubernetes/pull/135158), [@andrewsykim](https://github.com/andrewsykim)) [SIG Node] +- Graduate image volume source to on by default Beta ([#135195](https://github.com/kubernetes/kubernetes/pull/135195), [@haircommander](https://github.com/haircommander)) [SIG Apps, Instrumentation, Node and Testing] +- Implement scoring for DRA-backed extended resources ([#134058](https://github.com/kubernetes/kubernetes/pull/134058), [@bart0sh](https://github.com/bart0sh)) [SIG Node, Scheduling and Testing] +- KEP-3619: fined-grained supplemental groups policy is graduated to GA. ([#135088](https://github.com/kubernetes/kubernetes/pull/135088), [@everpeace](https://github.com/everpeace)) [SIG Node and Testing] +- KEP-5440: Allow for resizing of resources while job is suspended. This feature is alpha. ([#132441](https://github.com/kubernetes/kubernetes/pull/132441), [@kannon92](https://github.com/kannon92)) [SIG Apps and Testing] +- KEP-5598 opportunistic batching is implemented to optimize scheduling for pods that have the same scheduling requirements. ([#135231](https://github.com/kubernetes/kubernetes/pull/135231), [@bwsalmon](https://github.com/bwsalmon)) [SIG Node, Scheduling, Storage and Testing] +- Kubeadm: Add `HTTPEndpoints` field to `ClusterConfiguration.Etcd.ExternalEtcd` that can be used to configure the HTTP endpoints for etcd communication in v1beta4. This field is used to separate the HTTP traffic (such as /metrics and /health endpoints) from the gRPC traffic handled by Endpoints. This separation allows for better access control, as HTTP endpoints can be exposed without exposing the primary gRPC interface. Corresponds to etcd's `--listen-client-http-urls` configuration. If not provided, Endpoints will be used for both gRPC and HTTP traffic. ([#134890](https://github.com/kubernetes/kubernetes/pull/134890), [@SataQiu](https://github.com/SataQiu)) [SIG Cluster Lifecycle] +- Kubernetes is now built with go 1.25.4 ([#135187](https://github.com/kubernetes/kubernetes/pull/135187), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release] +- New metrics are introduced related to Ensure Secret Pulled Images KEP: + - kubelet_imagemanager_ondisk_pullintents - the number of pull intent records currently kept on disk + - kubelet_imagemanager_ondisk_pulledrecords - the number of image pulled records currently kept on disk + - kubelet_imagemanager_image_mustpull_checks_total{result} - the number for how many times an image was checked against the pull records and the results of those checks ([#132812](https://github.com/kubernetes/kubernetes/pull/132812), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- Pick one device class deterministically for extended resource when there are more than one ([#135037](https://github.com/kubernetes/kubernetes/pull/135037), [@yliaog](https://github.com/yliaog)) [SIG Node, Scheduling and Testing] +- Promoted the `EnvFiles` feature gate to beta and is enabled by default. Additionally, the syntax specification for environment variables has been restricted to a subset of POSIX shell syntax (all variable values must be wrapped in single quotes). ([#134414](https://github.com/kubernetes/kubernetes/pull/134414), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node and Testing] +- Promoted the `KubeletCrashLoopBackOffMax` feature gate to beta, it is now enabled by default. ([#135044](https://github.com/kubernetes/kubernetes/pull/135044), [@hankfreund](https://github.com/hankfreund)) [SIG Node] +- The Pod Certificates feature is moving to beta. The PodCertificateRequest feature gate is still set false by default. To use the feature, users will need to enable the certificates API groups in v1beta1 and enable the feature gate PodCertificateRequest. A new field UserAnnotations is added to the PodCertificateProjection API and the corresponding UnverifiedUserAnnotations is added to the PodCertificateRequest API. ([#134790](https://github.com/kubernetes/kubernetes/pull/134790), [@yt2985](https://github.com/yt2985)) [SIG Auth, Instrumentation and Testing] +- When resizing pods, more events will be emitted when the pod's resize status changes. ([#134825](https://github.com/kubernetes/kubernetes/pull/134825), [@natasha41575](https://github.com/natasha41575)) [SIG Node] + +### Bug or Regression + +- Extended resources requested by initContainers which are allocated using an automatic ResourceClaim now match the behavior of legacy device plugins, reusing the same resources requested by later sidecar initContainers or regular containers when possible, to minimize the total number of devices requested by the pod. ([#134882](https://github.com/kubernetes/kubernetes/pull/134882), [@yliaog](https://github.com/yliaog)) [SIG Apps, CLI, Node, Scheduling and Testing] +- 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. ([#135146](https://github.com/kubernetes/kubernetes/pull/135146), [@princepereira](https://github.com/princepereira)) [SIG Network and Windows] +- Fix bug in ValidatingAdmissionPolicy where a object schema with additionalProperties:true would crash the kube-controller-manager with a nil pointer exception. ([#135155](https://github.com/kubernetes/kubernetes/pull/135155), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery] +- Fixes an issue that disallowed restart policies and restart rules on static pods. ([#135031](https://github.com/kubernetes/kubernetes/pull/135031), [@yuanwang04](https://github.com/yuanwang04)) [SIG Node] +- Fixes the replacement tag in APIs to not be a selector for storage version ([#135197](https://github.com/kubernetes/kubernetes/pull/135197), [@Jefftree](https://github.com/Jefftree)) [SIG API Machinery] +- Kube-apiserver: Fixes spurious warning log messages about enabled alpha APIs while starting API server ([#135327](https://github.com/kubernetes/kubernetes/pull/135327), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery] +- Kubelet: fix concurrent map write error when creating a pod with empty volume when the LocalStorageCapacityIsolationFSQuotaMonitoring feature-gate is enabled ([#135174](https://github.com/kubernetes/kubernetes/pull/135174), [@carlory](https://github.com/carlory)) [SIG Storage] +- Support ShareID of DRAConsumableCapacity feature in the Kubelet Plugin API ([#134520](https://github.com/kubernetes/kubernetes/pull/134520), [@sunya-ch](https://github.com/sunya-ch)) [SIG Node and Testing] +- The slow initialization of container runtime will not cause System WatchDog to kill kubelet. Device Manager is not considered healthy before it attempted to start listening on the port. ([#135153](https://github.com/kubernetes/kubernetes/pull/135153), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) [SIG Node] +- Typed workqueue now cleans up goroutines before shutting down ([#135072](https://github.com/kubernetes/kubernetes/pull/135072), [@Jefftree](https://github.com/Jefftree)) [SIG API Machinery] + +### Other (Cleanup or Flake) + +- AggregatedDiscoveryRemoveBetaType feature gate is deprecated and locked to True ([#134230](https://github.com/kubernetes/kubernetes/pull/134230), [@Jefftree](https://github.com/Jefftree)) [SIG API Machinery] +- Dropped support for networking/v1beta1 Ingress in kubectl ([#135176](https://github.com/kubernetes/kubernetes/pull/135176), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Dropped support for networking/v1beta1 IngressClass in kubectl ([#135108](https://github.com/kubernetes/kubernetes/pull/135108), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Upgrade CoreDNS to v1.12.4 ([#133968](https://github.com/kubernetes/kubernetes/pull/133968), [@yashsingh74](https://github.com/yashsingh74)) [SIG Cloud Provider and Cluster Lifecycle] + +## Dependencies + +### Added +- cyphar.com/go-pathrs: v0.2.1 + +### Changed +- github.com/coredns/corefile-migration: [v1.0.27 → v1.0.29](https://github.com/coredns/corefile-migration/compare/v1.0.27...v1.0.29) +- github.com/cyphar/filepath-securejoin: [v0.4.1 → v0.6.0](https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...v0.6.0) +- github.com/opencontainers/selinux: [v1.11.1 → v1.13.0](https://github.com/opencontainers/selinux/compare/v1.11.1...v1.13.0) + +### Removed +_Nothing has changed._ + + + +# v1.35.0-alpha.3 + + +## Downloads for v1.35.0-alpha.3 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes.tar.gz) | 054e77631e6a17dcb1589e14aaf215672c054a3315de0e72fad066d5f4392ff09288dc0ead2e9667c65c3c7c770d81206abb94eaf2615b1ef0cc99fbf3a5c793 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-src.tar.gz) | fe30a5b352bb1656d7306aec0f491fde6f874af7d749fa31fe75ac5035c98d3c63d95db1b0c0024b30c55eadf7b60a1c3513a343eff2d6b0793147112940c82b + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-darwin-amd64.tar.gz) | ca86f0ff39c9ee9ddf75674369cac952652afb3d36c11d8b761d00e9a6f9827adda24d87db6d936ab4ff54cd3d65afcc1e8b77868bc8054837d36cc9725a0fe8 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-darwin-arm64.tar.gz) | b5a6772bfd7fd59ad18d0ccd6cece28d316613c1364607bdcb6389b2be1e911297b8ea3fb4b0ced7c38e66be36bf3f42898e4a5fade67add6a29cc5caec0f449 +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-386.tar.gz) | 8ecf519056385911fcec30039c8c3bf8537726c35ad9637602444dc6f1c5cc4f34fd2b924641b64b5a94b81935deff3a1445bc161fa3c3887a26b6a572e5a126 +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-amd64.tar.gz) | 499d946c3baf4bf55cc12ae0166ebbd3ae2c0c383d0f0cabca18cdc843b101e4fe0a972117f01d59e6eb61056471bdf5ff7b1c124e42298a4d758c01f8d888dc +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-arm.tar.gz) | fc240f0e23fad7578330cfb65ee271b3dfee099fd4fea3df6e5bd6cd5c50d8d398915d3c1dd735593b96ed1f3d30a800dd3ee6b1553c32bbc46428823ff68d6d +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-arm64.tar.gz) | ee4a49a4c55d9fce0cddf8150fa506df2c498abb257bb87c773260c26dc32fcb14be97630a27a22dd7406207778c7111f95751dc80b5dfabdf0755757b9c7082 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-ppc64le.tar.gz) | 758eab71ab6435a689ac081bad27270967b6f8a09532b2dbc1c45b16eb8cc9ee24d317c4c8adf4345c569e89a1edeea8fb6f1bf97f7f84604fc17a7459f9a59f +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-linux-s390x.tar.gz) | 13947dfc8a67de7805e2e0818452d287079f9f382c8e36e8501b0871c5083f3eef1ac0461ca3570abeb39f84391b75843236a98f56f17a75f01a3d88cbfc6998 +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-windows-386.tar.gz) | 813f670f33f20dfbec2dfd53136831f1117b5d172fa381fb1f69348d9f2e1cdda5eff2f807529924d1751d31011f3ba0a9dfd2e395114f8c289cbc3a262a207b +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-windows-amd64.tar.gz) | 6cfafe20404a2d6d8d7f9ed923eabc59360ba16454db8602de7aaaf3f40af7ff0429f54c3a34fd8c94d4a2e83bffeab29de5eed78f45f3cbe4027a8ae23a25c9 +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-client-windows-arm64.tar.gz) | dc4f018a9d7182c32f82727e42624f6b5883e944a730855ab0dd9ab9e8a5eea0766c5214ce5bf63c3bafc795d28bc335a94c9e50f1c4c80887c944780bb7811a + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-server-linux-amd64.tar.gz) | 741bc1b0cb536ae82284b299fbb27c466e7ce3b54ba879a40631c5c00d822bce76dbb927d51ccb50383f22e115c4f0a5d22d8157cd9ea69da797f2fae2229b50 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-server-linux-arm64.tar.gz) | 26d073a93c26511aa3ec2e47954193175a87426d6f489370cbc5d2cbc636e98785a8c065d3cee1e3fcd52f4ee2b37e3137ade65739704b4aa3582c41d9e69341 +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-server-linux-ppc64le.tar.gz) | 2ce67d08040129bc1d290faf63573f7e1881f2ec7eaf02a4a27cbd48285fc315ff336d245c63f6bb8dd5b2e82821beed731bf9e9f807a4d5a0fadac355413183 +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-server-linux-s390x.tar.gz) | 6b12151c9ab895a9c51f7e17b067d165b511f8c7e32c5ee2cb9924087314bcacd74826e1b18bccd1e06b85a9a3c26e151c38fed9a4f40794777bd06f68cb3e95 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-node-linux-amd64.tar.gz) | f13943abe46974a701c1de6a20f76a2ade96db4795de7f6615680c3a360d602d5efca1d062c206f5154e3c3f504c0e51fda10e96ed31e20b3bd3d711be3600f8 +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-node-linux-arm64.tar.gz) | b7b664dde1dea0469dcab0a8f30032c583210d008621580930feb4a56353f9d51b732643fb41600febae3da3f2f17617c7914487539e4d7be8b4942c52219c85 +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-node-linux-ppc64le.tar.gz) | 38cc958f6bc855b9fb6da6dbe1dd4eda874916865b030b929eab5f4110fa9554d7531757992e54ad912ca41d7eee6f01e6a299132c023199d7751491ae5456da +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-node-linux-s390x.tar.gz) | c32b6d753f1c76bfcac7c37d5986d243cb5b7ad6bd01596b84d4262250ba31d875005302e8b92f7b8bac9ad30a85a6a60f99609fbbcceb0df1d08dfad8539488 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.3/kubernetes-node-windows-amd64.tar.gz) | c5dfdcf501f39003ff818fa66c90e3874d9db21afc74ce9d6fe20de6f074d755ee0a90e18e47ffebda0da5685e9b42e8385f6e7e3d518b08a911c019686257d9 + +### 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.35.0-alpha.3](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.35.0-alpha.3](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.35.0-alpha.3](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.35.0-alpha.3](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.35.0-alpha.3](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.35.0-alpha.3](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.35.0-alpha.2 + +## Urgent Upgrade Notes + +### (No, really, you MUST read this before you upgrade) + + - ACTION REQUIRED + + vendor: updated k8s.io/system-validators to v1.12.1. The cgroups validator will now throw an error instead of a warning if cgroups v1 is detected on the host and the provided KubeletVersion is 1.35 or newer. + + kubeadm: started using k8s.io/system-validators v1.12.1 in kubeadm 1.35. During `kubeadm init`, `kubeadm join` and `kubeadm upgrade`, the SystemVerification preflight check will throw an error if cgroups v1 is detected and if the detected kubelet version is 1.35 or newer. For older versions of kubelet, there will be just a preflight warning. + + To allow cgroups v1 with kubeadm and kubelet version 1.35 or newer, you must: + - Ignore the error from the SystemVerifcation preflight check by kubeadm. + - Edit the kube-system/kubelet-config ConfigMap and add the `failCgroupV1: false` field, before upgrading. ([#134744](https://github.com/kubernetes/kubernetes/pull/134744), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle and Node] + - Removed the `--pod-infra-container-image` flag from kubelet's command line. For non-kubeadm clusters, users must manually remove this flag from their kubelet configuration to prevent startup failures before they upgrade kubelet. ([#133779](https://github.com/kubernetes/kubernetes/pull/133779), [@carlory](https://github.com/carlory)) [SIG Node] + +## Changes by Kind + +### API Change + +- Add ObservedGeneration to CustomResourceDefinition Conditions. ([#134984](https://github.com/kubernetes/kubernetes/pull/134984), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery] +- Add StorageVersionMigration v1beta1 api and remove the v1alpha API. + + Any use of the v1alpha1 api is no longer supported and + users must remove any v1alpha1 resources prior to upgrade. ([#134784](https://github.com/kubernetes/kubernetes/pull/134784), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Apps, Auth, Etcd and Testing] +- CSI drivers can now opt-in to receive service account tokens via the secrets field instead of volume context by setting `spec.serviceAccountTokenInSecrets: true` in the CSIDriver object. This prevents tokens from being exposed in logs and other outputs. The feature is gated by the `CSIServiceAccountTokenSecrets` feature gate (Beta in v1.35). ([#134826](https://github.com/kubernetes/kubernetes/pull/134826), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth, Storage and Testing] +- DRA device taints: DeviceTaintRule status provided information about the rule, in particular whether pods still need to be evicted ("EvictionInProgress" condition). The new "None" effect can be used to preview what a DeviceTaintRule would do if it used the "NoExecute" effect and to taint devices ("device health") without immediately affecting scheduling or running pods. ([#134152](https://github.com/kubernetes/kubernetes/pull/134152), [@pohly](https://github.com/pohly)) [SIG API Machinery, Apps, Auth, Node, Release, Scheduling and Testing] +- DRA: the DynamicResourceAllocation feature gate for the core functionality (GA in 1.34) is now locked to enabled-by-default and thus cannot be disabled anymore. ([#134452](https://github.com/kubernetes/kubernetes/pull/134452), [@pohly](https://github.com/pohly)) [SIG Auth, Node, Scheduling and Testing] +- Forbid adding resources other than CPU & memory on pod resize. ([#135084](https://github.com/kubernetes/kubernetes/pull/135084), [@tallclair](https://github.com/tallclair)) [SIG Apps, Node and Testing] +- Implement constrained impersonation as described in https://kep.k8s.io/5284 ([#134803](https://github.com/kubernetes/kubernetes/pull/134803), [@enj](https://github.com/enj)) [SIG API Machinery, Auth and Testing] +- Introduces a structured and versioned v1alpha1 response for flagz ([#134995](https://github.com/kubernetes/kubernetes/pull/134995), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- Introduces a structured and versioned v1alpha1 response for statusz ([#134313](https://github.com/kubernetes/kubernetes/pull/134313), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- New `--min-compatibility-version` flag for apiserver, kcm and kube scheduler ([#133980](https://github.com/kubernetes/kubernetes/pull/133980), [@siyuanfoundation](https://github.com/siyuanfoundation)) [SIG API Machinery, Architecture, Cluster Lifecycle, Etcd, Scheduling and Testing] +- Promote PodObservedGenerationTracking to GA. ([#134948](https://github.com/kubernetes/kubernetes/pull/134948), [@natasha41575](https://github.com/natasha41575)) [SIG API Machinery, Apps, Node, Scheduling and Testing] +- Promoted Job Managed By to general availability. The `JobManagedBy` feature gate is now locked to true, and will be removed in a future release of Kubernetes. ([#135080](https://github.com/kubernetes/kubernetes/pull/135080), [@dejanzele](https://github.com/dejanzele)) [SIG API Machinery, Apps and Testing] +- Promoted ReplicaSet and Deployment `.status.terminatingReplicas` tracking to beta. The `DeploymentReplicaSetTerminatingReplicas` feature gate is now enabled by default. ([#133087](https://github.com/kubernetes/kubernetes/pull/133087), [@atiratree](https://github.com/atiratree)) [SIG API Machinery, Apps and Testing] +- Scheduler: added a new `bindingTimeout` argument to the DynamicResources plugin configuration. + This allows customizing the wait duration in PreBind for device binding conditions. + Defaults to 10 minutes when DRADeviceBindingConditions and DRAResourceClaimDeviceStatus are both enabled. ([#134905](https://github.com/kubernetes/kubernetes/pull/134905), [@fj-naji](https://github.com/fj-naji)) [SIG Node and Scheduling] +- The Pod Certificates feature is moving to beta. The PodCertificateRequest feature gate is still set false by default. To use the feature, users will need to enable the certificates API groups in v1beta1 and enable the feature gate PodCertificateRequest. A new field UserAnnotations is added to the PodCertificateProjection API and the corresponding UnverifiedUserAnnotations is added to the PodCertificateRequest API. ([#134624](https://github.com/kubernetes/kubernetes/pull/134624), [@yt2985](https://github.com/yt2985)) [SIG API Machinery, Apps, Auth, Etcd, Instrumentation, Node and Testing] +- The StrictCostEnforcementForVAP and StrictCostEnforcementForWebhooks feature gates, locked on since 1.32, have been removed ([#134994](https://github.com/kubernetes/kubernetes/pull/134994), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth, Node and Testing] +- The `PreferSameZone` and `PreferSameNode` values for Service's + `trafficDistribution` field are now GA. The old value `PreferClose` is now + deprecated in favor of the more-explicit `PreferSameZone`. ([#134457](https://github.com/kubernetes/kubernetes/pull/134457), [@danwinship](https://github.com/danwinship)) [SIG API Machinery, Apps, Network and Testing] + +### Feature + +- Add the `ChangeContainerStatusOnKubeletRestart` feature gate. The feature gate defaults to disabled. When the feature gate is disabled, the kubelet does not change the pod status upon restart, and pods will not re-run startup probes after kubelet restart. ([#134746](https://github.com/kubernetes/kubernetes/pull/134746), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node and Testing] +- Added a new `source` label in `resourceclaim_controller_resource_claims`. + Added a new metrics for DRAExtendedResource `scheduler_resourceclaim_creates_total`. ([#134523](https://github.com/kubernetes/kubernetes/pull/134523), [@bitoku](https://github.com/bitoku)) [SIG Apps, Instrumentation, Node and Scheduling] +- Added support for tracing in kubectl with --profile=trace ([#134709](https://github.com/kubernetes/kubernetes/pull/134709), [@tchap](https://github.com/tchap)) [SIG CLI] +- Adding new kuberc view/set commands in kubectl to perform operations against kuberc file ([#135003](https://github.com/kubernetes/kubernetes/pull/135003), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] +- Enable MutableCSINodeAllocatableCount by default. ([#134647](https://github.com/kubernetes/kubernetes/pull/134647), [@torredil](https://github.com/torredil)) [SIG Storage] +- Improved throughput in the real-FIFO queue used by informer/controllers by adding batch handling for processing watch events. ([#132240](https://github.com/kubernetes/kubernetes/pull/132240), [@yue9944882](https://github.com/yue9944882)) [SIG API Machinery, Scheduling and Storage] +- Introducing new flag --as-user-extra persistent flag in kubectl that can be used to pass extra arguments during the impersonation ([#134378](https://github.com/kubernetes/kubernetes/pull/134378), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] +- Kube-apiserver: JWT authenticator now report the following metrics: + - apiserver_authentication_jwt_authenticator_jwks_fetch_last_timestamp_seconds + - apiserver_authentication_jwt_authenticator_jwks_fetch_last_key_set_info + + when StructuredAuthenticationConfiguration feature is enabled. ([#123642](https://github.com/kubernetes/kubernetes/pull/123642), [@aramase](https://github.com/aramase)) [SIG API Machinery, Auth and Testing] +- Kubeadm: added a new preflight check `ContainerRuntimeVersion ` to validate if the installed container runtime supports the RuntimeConfig gRPC method. If the container runtime does not support the RuntimeConfig gRPC method, kubeadm will print a warning message. + + Once Kubernetes 1.36 is released, the kubelet might refuse to start if the CRI runtime does not support this feature. More information can be found in https://kubernetes.io/blog/2025/09/12/kubernetes-v1-34-cri-cgroup-driver-lookup-now-ga/. ([#134906](https://github.com/kubernetes/kubernetes/pull/134906), [@carlory](https://github.com/carlory)) [SIG Cluster Lifecycle] +- New counter metric exposing details about kubelet ensuring an image exists on the node is added - `kubelet_image_manager_ensure_image_requests_total{present_locally, pull_policy, pull_required}` ([#132644](https://github.com/kubernetes/kubernetes/pull/132644), [@stlaz](https://github.com/stlaz)) [SIG Auth and Node] +- Promote InPlacePodVerticalScaling to GA. ([#134949](https://github.com/kubernetes/kubernetes/pull/134949), [@natasha41575](https://github.com/natasha41575)) [SIG API Machinery, Node and Scheduling] +- Promote Relaxed validation for Services names to beta (enabled by default) + + Promote `RelaxedServiceNameValidation` feature to beta (enabled by default) + The names of new Services names are validation with `NameIsDNSLabel()`, + relaxing the pre-existing validation. ([#134493](https://github.com/kubernetes/kubernetes/pull/134493), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Network] +- Promote kubectl command headers to stable ([#134777](https://github.com/kubernetes/kubernetes/pull/134777), [@soltysh](https://github.com/soltysh)) [SIG CLI and Testing] +- The SchedulerAsyncAPICalls feature gate has been re-enabled by default after fixing regressions detected in v1.34. ([#135059](https://github.com/kubernetes/kubernetes/pull/135059), [@macsko](https://github.com/macsko)) [SIG Scheduling] +- The scheduler clears the `nominatedNodeName` field for Pods upon scheduling or binding failure. External components, such as Cluster Autoscaler and Karpenter, should not overwrite this field. ([#135007](https://github.com/kubernetes/kubernetes/pull/135007), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] + +### Bug or Regression + +- BlockOwnerDeletion is removed from resource claims created from resource claim templates, and extended resource claims created by scheduler ([#134956](https://github.com/kubernetes/kubernetes/pull/134956), [@yliaog](https://github.com/yliaog)) [SIG Apps, Node and Scheduling] +- Drop DeviceBindingConditions fields if the DRADeviceBindingConditions is not enabled and not in-use ([#134964](https://github.com/kubernetes/kubernetes/pull/134964), [@sunya-ch](https://github.com/sunya-ch)) +- Fix a very old issue where kubelet rejects pods with NodeAffinityFailed due to a stale informer cache. ([#134445](https://github.com/kubernetes/kubernetes/pull/134445), [@natasha41575](https://github.com/natasha41575)) [SIG Node] +- Fix issue in asynchronous preemption: Scheduler checks if preemption is ongoing for a pod before initiating new preemption calls ([#134730](https://github.com/kubernetes/kubernetes/pull/134730), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] +- Fix panic on kubectl api-resources ([#134833](https://github.com/kubernetes/kubernetes/pull/134833), [@rikatz](https://github.com/rikatz)) [SIG CLI] +- Fix setting distinctAttribute=nil when DRAConsumableCapacity is disabled ([#134962](https://github.com/kubernetes/kubernetes/pull/134962), [@sunya-ch](https://github.com/sunya-ch)) [SIG Node] +- 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. ([#134769](https://github.com/kubernetes/kubernetes/pull/134769), [@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. ([#134793](https://github.com/kubernetes/kubernetes/pull/134793), [@LionelJouin](https://github.com/LionelJouin)) [SIG Node] +- Fixed a bug that caused a deleted pod staying in the binding phase to occupy space on the node in the kube-scheduler. ([#134157](https://github.com/kubernetes/kubernetes/pull/134157), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] +- Fixed a bug that prevent allocating the same device that was previously consuming the CounterSet when enabling both DRAConsumableCapacity and DRAPartitionableDevices. ([#134103](https://github.com/kubernetes/kubernetes/pull/134103), [@sunya-ch](https://github.com/sunya-ch)) [SIG Node] +- Fixed a bug where the health of a DRA resource was not reported in the Pod status if the resource claim was generated from a template or used a different local name in the pod spec. ([#134875](https://github.com/kubernetes/kubernetes/pull/134875), [@Jpsassine](https://github.com/Jpsassine)) [SIG Node and Testing] +- Fixes an issue where the kubelet /configz endpoint reported incorrect value for kubeletconfig.cgroupDriver when the cgroup driver setting is received from the container runtime. ([#134743](https://github.com/kubernetes/kubernetes/pull/134743), [@marquiz](https://github.com/marquiz)) [SIG Node] +- Fixes bug where AllocationMode: All would not succeed if a resource pool contained ResourceSlices that wasn't targeting the current node. ([#134466](https://github.com/kubernetes/kubernetes/pull/134466), [@mortent](https://github.com/mortent)) [SIG Node] +- 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. ([#135017](https://github.com/kubernetes/kubernetes/pull/135017), [@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 ([#134740](https://github.com/kubernetes/kubernetes/pull/134740), [@hoskeri](https://github.com/hoskeri)) [SIG Scheduling] +- Namespace is added to the output of dry-run=client of HPA object ([#134263](https://github.com/kubernetes/kubernetes/pull/134263), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI and Testing] + +### Other (Cleanup or Flake) + +- Added a new filed `Step` in the testing framework to allow volume expansion in configurable step sizes for tests. ([#134760](https://github.com/kubernetes/kubernetes/pull/134760), [@Rishita-Golla](https://github.com/Rishita-Golla)) [SIG Storage and Testing] +- Dropped support for certificates/v1beta1 CertificateSigningRequest in kubectl ([#134782](https://github.com/kubernetes/kubernetes/pull/134782), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Dropped support for discovery/v1beta1 EndpointSlice in kubectl ([#134913](https://github.com/kubernetes/kubernetes/pull/134913), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Dropped support for networking/v1beta1 IngressClass in kubectl ([#135108](https://github.com/kubernetes/kubernetes/pull/135108), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Eliminate use of md5 and prevent future use of md5 in favor of more appropriate hashing algorithms. ([#133511](https://github.com/kubernetes/kubernetes/pull/133511), [@BenTheElder](https://github.com/BenTheElder)) [SIG Apps, Architecture, CLI, Cluster Lifecycle, Network, Node, Security, Storage and Testing] +- Kubeadm: removed the kubeadm-specific feature gate WaitForAllControlPlaneComponents which graduated to GA in 1.34 and was locked to enabled by default. ([#134781](https://github.com/kubernetes/kubernetes/pull/134781), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: updated the supported etcd version to v3.5.24 for supported control plane versions v1.32, v1.33, and v1.34. ([#134779](https://github.com/kubernetes/kubernetes/pull/134779), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- Migrate the cpumanager to contextual logging ([#125912](https://github.com/kubernetes/kubernetes/pull/125912), [@ffromani](https://github.com/ffromani)) [SIG Node] +- Removed the `UserNamespacesPodSecurityStandards` feature gate. The minimum supported Kubernetes version for a kubelet is now v1.31, so the gate is not needed. ([#132157](https://github.com/kubernetes/kubernetes/pull/132157), [@haircommander](https://github.com/haircommander)) [SIG Auth, Node and Testing] +- The FeatureGate SystemdWatchdog is locked to default and will be removed. The Systemd Watchdog functionality in kubelet can be turned on via Systemd without any feature gate set up. See https://kubernetes.io/docs/reference/node/systemd-watchdog/ for information. ([#134691](https://github.com/kubernetes/kubernetes/pull/134691), [@SergeyKanzhelev](https://github.com/SergeyKanzhelev)) [SIG Node] +- Updates the etcd client library to v3.6.5 ([#134780](https://github.com/kubernetes/kubernetes/pull/134780), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] + +## Dependencies + +### Added +- github.com/Masterminds/semver/v3: [v3.4.0](https://github.com/Masterminds/semver/tree/v3.4.0) +- github.com/gkampitakis/ciinfo: [v0.3.2](https://github.com/gkampitakis/ciinfo/tree/v0.3.2) +- github.com/gkampitakis/go-diff: [v1.3.2](https://github.com/gkampitakis/go-diff/tree/v1.3.2) +- github.com/gkampitakis/go-snaps: [v0.5.15](https://github.com/gkampitakis/go-snaps/tree/v0.5.15) +- github.com/goccy/go-yaml: [v1.18.0](https://github.com/goccy/go-yaml/tree/v1.18.0) +- github.com/joshdk/go-junit: [v1.0.0](https://github.com/joshdk/go-junit/tree/v1.0.0) +- github.com/maruel/natural: [v1.1.1](https://github.com/maruel/natural/tree/v1.1.1) +- github.com/mfridman/tparse: [v0.18.0](https://github.com/mfridman/tparse/tree/v0.18.0) +- github.com/tidwall/gjson: [v1.18.0](https://github.com/tidwall/gjson/tree/v1.18.0) +- github.com/tidwall/match: [v1.1.1](https://github.com/tidwall/match/tree/v1.1.1) +- github.com/tidwall/pretty: [v1.2.1](https://github.com/tidwall/pretty/tree/v1.2.1) +- github.com/tidwall/sjson: [v1.2.5](https://github.com/tidwall/sjson/tree/v1.2.5) +- go.uber.org/automaxprocs: v1.6.0 + +### Changed +- github.com/google/pprof: [d1b30fe → 27863c8](https://github.com/google/pprof/compare/d1b30fe...27863c8) +- github.com/onsi/ginkgo/v2: [v2.21.0 → v2.27.2](https://github.com/onsi/ginkgo/compare/v2.21.0...v2.27.2) +- github.com/onsi/gomega: [v1.35.1 → v1.38.2](https://github.com/onsi/gomega/compare/v1.35.1...v1.38.2) +- github.com/rogpeppe/go-internal: [v1.13.1 → v1.14.1](https://github.com/rogpeppe/go-internal/compare/v1.13.1...v1.14.1) +- go.etcd.io/bbolt: v1.4.2 → v1.4.3 +- go.etcd.io/etcd/api/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/client/pkg/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/client/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/pkg/v3: v3.6.4 → v3.6.5 +- go.etcd.io/etcd/server/v3: v3.6.4 → v3.6.5 +- go.yaml.in/yaml/v2: v2.4.2 → v2.4.3 +- golang.org/x/mod: v0.27.0 → v0.28.0 +- golang.org/x/sync: v0.16.0 → v0.17.0 +- golang.org/x/sys: v0.35.0 → v0.37.0 +- golang.org/x/term: v0.34.0 → v0.36.0 +- golang.org/x/text: v0.28.0 → v0.29.0 +- k8s.io/system-validators: v1.11.1 → v1.12.1 +- k8s.io/utils: 4c0f3b2 → bc988d5 + +### Removed +_Nothing has changed._ + + + +# v1.35.0-alpha.2 + + +## Downloads for v1.35.0-alpha.2 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes.tar.gz) | acba342356249738a81bf6bc6de95e4a30097fdd0ebe956b8cd8a2b0715e3161930f7408bd3b1ca1e05c07de4359485cf887b278987366efef3caf9024e80c6d +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-src.tar.gz) | 6e9f58180f53e57ae6b462d4ab3a13f7cafc9bb9802f8af3254e9f3c78b9883103972dced5dd0796c9c8e4176fd8557754981a63fc4b5eb4fb0d07838027ac70 + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-darwin-amd64.tar.gz) | cb54b9aa876b327915048fa3d9a152abcde442d60cee750566339335b19c668f1d440f1dd79409137e7ee5d7e32e2d3c6e8b3fcaf7f4932b19508b483e3d4172 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-darwin-arm64.tar.gz) | 600f2922a818c9c750269695b9158892fcfdd1dd1311701033f93b396689c7d4625c24880598ea36ca3d1ff76be53dcdff911a96d8f337ec93847e340639a92b +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-386.tar.gz) | bcc1d2c3b5577b22636b7c9aa515fb9944e586d5ae657e066e204388992bb1e9c94dd54ecc7feaaafe46c89943e5500366a26dac11ee2eb32ea3106daf1da51b +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-amd64.tar.gz) | 4250b2063c70cd69b49a50a4a416a9bd5a4e7734ed8b9ccc1081ed12e23c30018c2be9dc377100eb14823bab26aa33670e92d7ba38588a2a0ca011c3d63ecbf5 +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-arm.tar.gz) | 203825398afd6c697ac2fc13b126d7419b1c108362e6bb8a27eddef57e2845dd02735e4c48a5c2aa813f9e0ce24ee97ae94360cf50a9197fba53ba3ac736a50e +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-arm64.tar.gz) | b6669df8c4e096d7ca435bfa481823b74e131907433fc7b7dbf6e6a699f2905a60c98e3c23c9321462ae3afdd707ffea3acf473a13905e63203cebefa80028c2 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-ppc64le.tar.gz) | 37b0ce0d3dfa8dcd2222c63b6572e32ad1a7f07d4164de886b3eca04d4c655a3cc07786090eb24cc20f0bf641cae2efba7ab3c3cd2da5536575571db31aa89da +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-linux-s390x.tar.gz) | 3d7054c4b8d18501b535b0cd070bab316b7393bcb575fc869e2fde7190044b15a42e32dbea6aee64aef933ec1d8c7c11581c61bcb4829e710b26971b133180c3 +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-windows-386.tar.gz) | ae011d1aa7b41160d50b9cd9bc4fe2890bbc2ce2f2b6c63695ae20f36e93cbf189c32deafc0d99c46532917ba291f40965cd4038edcb5bb3a27cd66974dba539 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-windows-amd64.tar.gz) | bcff5d410cea98ab7e83a66c545b4322d17055ed0b3c7acb110a757e6f0ee55aadfc0174c8c641511ac832024af5b2660f4e2be5c3076a12e0b862aa55a1d02f +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-client-windows-arm64.tar.gz) | 4fb60b2747b500f1139f590e436318fabdd692fc7d2de27be9667c1e5f9af3a6a67796fcd3c69b92e225a04ae92292d715c4c5e1a1437f1723e5bd16d30e5c59 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-server-linux-amd64.tar.gz) | b29aaf01ad35edf7d24ac2a1d493c28a65941fd9f490bbcaeecfc418b1e26060f90e1677353ace6229ae1b8416f5080e116fcfb90732a7aab094761d9f1dadbe +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-server-linux-arm64.tar.gz) | fc8aeaec77c22d1cb777d9d626f6cbdc0bd178a29d1c125305592d4b40680c51d30d6ffeeb5754abd029c56ed2f49462a85799939f7ffc343f4816da3b9a2d20 +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-server-linux-ppc64le.tar.gz) | 61a70fa842e8afff5bdf3ab45a85b0bae183eb0e3910c440ca21520d3f03e0ee66ffbdd8b335b0d9ccfd2c77fb1a82e0f1480a267da6e6c10255c87465b12965 +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-server-linux-s390x.tar.gz) | 9275068613ddfaa163bbfaed5ae0c69dc2ca2031b3f42f990ed42995b14e7d5ed1bd5d49d3c3b7e95f7024a4434cb3518e05240b82d508f2be2c6d4971d3ab43 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-node-linux-amd64.tar.gz) | 2200656cfe27817ec8bfc67564fba75c0afb582c75b2ce37734dff1c2757d142d45a24695c7e898b4663362f4058ca0ae8399ee485883833498cac9867caccdc +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-node-linux-arm64.tar.gz) | fe087958b7b7b7197132473508deffa90a740fffc2bf7a06c9a7c7df029394fd27a307efc6bb8003c6f95d9013f57ed577ec4a777881c44acba26a1ebc918ae5 +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-node-linux-ppc64le.tar.gz) | 621a7d6f7f3fcc382922f0912a5dd3f9587ec15992c65be806a18e4b3254895d42dc78ac2b1aab10a16dee1227ca315c1b5b35c27b29946c1337d548b799ddc7 +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-node-linux-s390x.tar.gz) | 16a9074fba6db7ef45b38ed5ea05ae9cd47a6388b01cfa551377de5bc1b720df3507b8bda974c1220d8a82394902192a4013754026f2d71732bb480743862c05 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.2/kubernetes-node-windows-amd64.tar.gz) | 6a893a6a4ad7f664fea7536f22bd27d4f874e7568658f5863e156c290b4afee2ef566797c6e3dae86b4d706f219b8169da5e03ef61e565b7a4ba2123a6b43c5c + +### 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.35.0-alpha.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.35.0-alpha.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.35.0-alpha.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.35.0-alpha.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.35.0-alpha.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.35.0-alpha.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.35.0-alpha.1 + +## Changes by Kind + +### Deprecation + +- FailCgroupV1 will be set to true from 1.35. + This means that nodes will not start on a cgroup v1 in our default behavior. + This is putting cgroup v1 into a deprecated state. ([#134298](https://github.com/kubernetes/kubernetes/pull/134298), [@kannon92](https://github.com/kannon92)) [SIG Node] +- Mark ipvs mode in kube-proxy as deprecated. ipvs mode in kube-proxy is deprecated and will be removed in a future version of Kubernetes. Users are encouraged to move to nftables. ([#134539](https://github.com/kubernetes/kubernetes/pull/134539), [@adrianmoisey](https://github.com/adrianmoisey)) [SIG Network] + +### API Change + +- Kube-apiserver: fix a possible panic validating a custom resource whose CustomResourceDefinition indicates a status subresource exists, but which does not define a `status` property in the `openAPIV3Schema` ([#133721](https://github.com/kubernetes/kubernetes/pull/133721), [@fusida](https://github.com/fusida)) [SIG API Machinery, Apps, Architecture, Auth, Autoscaling, CLI, Cloud Provider, Cluster Lifecycle, Etcd, Instrumentation, Network, Node, Release, Scheduling, Storage and Testing] +- Kubernetes API Go types removed runtime use of the github.com/gogo/protobuf library, and are no longer registered into the global gogo type registry. Kubernetes API Go types were not suitable for use with the google.golang.org/protobuf library, and no longer implement `ProtoMessage()` by default to avoid accidental incompatible use. If removal of these marker methods impacts your use, it can be re-enabled for one more release with a `kubernetes_protomessage_one_more_release` build tag, but will be removed in 1.36. ([#134256](https://github.com/kubernetes/kubernetes/pull/134256), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling and Storage] +- Promoted HPA configurable tolerance to beta. The `HPAConfigurableTolerance` feature gate is now enabled by default. ([#133128](https://github.com/kubernetes/kubernetes/pull/133128), [@jm-franc](https://github.com/jm-franc)) [SIG API Machinery and Autoscaling] +- The MaxUnavailableStatefulSet feature is now beta and enabled by default. ([#133153](https://github.com/kubernetes/kubernetes/pull/133153), [@helayoty](https://github.com/helayoty)) [SIG API Machinery and Apps] + +### Feature + +- Enable the feature gate `ContainerRestartRules` by default. The ContainerRestartRules feature is promoted to beta. Fixing a bug in this feature that caused probes continue to run even if the container has terminated and is not restartable. ([#134631](https://github.com/kubernetes/kubernetes/pull/134631), [@yuanwang04](https://github.com/yuanwang04)) [SIG Node] +- Kube-apiserver: the subresources `pods/exec`, `pods/attach`, and `pods/portforward` now require `create` permission for both SPDY and Websocket API requests. Previously, SPDY requests required `create` permission, but Websocket requests only required `get` permission. This change is gated by the `AuthorizePodWebsocketUpgradeCreatePermission` feature-gate, which is enabled by default. + + Before upgrading to 1.35, ensure any custom ClusterRoles and Roles intended to grant `pods/exec`, `pods/attach`, or `pods/portforward` permission include the `create` verb. ([#134577](https://github.com/kubernetes/kubernetes/pull/134577), [@seans3](https://github.com/seans3)) [SIG API Machinery, Auth, Node and Testing] +- Kubeadm: print the errors during retires related to the WaitForAllControlPlaneComponents functionality at verbosity level 5. ([#134433](https://github.com/kubernetes/kubernetes/pull/134433), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubernetes is now built using Go 1.25.3 ([#134611](https://github.com/kubernetes/kubernetes/pull/134611), [@cpanato](https://github.com/cpanato)) [SIG Architecture, Cloud Provider, Etcd, Release, Storage and Testing] +- Locked the (generally available) feature gate `ExecProbeTimeout` to true. ([#134635](https://github.com/kubernetes/kubernetes/pull/134635), [@vivzbansal](https://github.com/vivzbansal)) [SIG Node and Testing] +- Promoted the `HostnameOverride` feature gate to beta and is enabled by default. ([#134729](https://github.com/kubernetes/kubernetes/pull/134729), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Network and Node] + +### Documentation + +- Kubectl describe, get, drain and events have the ability to set chunk-size using --chunk-size flag, which is now officially stable. ([#134481](https://github.com/kubernetes/kubernetes/pull/134481), [@soltysh](https://github.com/soltysh)) [SIG CLI] + +### Bug or Regression + +- DRA API: the "tolerations" field in exact and sub requests now gets dropped properly when the DRADeviceTaints API is disabled. ([#132927](https://github.com/kubernetes/kubernetes/pull/132927), [@pohly](https://github.com/pohly)) +- DRA Device Taints: tolerating a NoExecute did not work because the scheduler did not inform the eviction controller about the toleration, so the scheduled pod got evicted almost immediately. ([#134479](https://github.com/kubernetes/kubernetes/pull/134479), [@pohly](https://github.com/pohly)) [SIG Apps, Node, Scheduling and Testing] +- Endpoints/endpointslice controllers perform much better when there are a large number of services in a single namespace ([#134739](https://github.com/kubernetes/kubernetes/pull/134739), [@shyamjvs](https://github.com/shyamjvs)) [SIG Apps and Network] +- Fixed a bug that prevents schedule next pod when using DRAConsumableCapacity feature. (#133705, @sunya-ch) ([#133706](https://github.com/kubernetes/kubernetes/pull/133706), [@sunya-ch](https://github.com/sunya-ch)) [SIG Node] +- Fixed a bug where 64 bit IPv6 ServiceCIDRs allocated addresses outside the subnet range. ([#134193](https://github.com/kubernetes/kubernetes/pull/134193), [@hoskeri](https://github.com/hoskeri)) [SIG Network] +- 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. ([#133072](https://github.com/kubernetes/kubernetes/pull/133072), [@AadiDev005](https://github.com/AadiDev005)) [SIG Node and Testing] +- Kube-apiserver: when --requestheader-client-ca-file and --client-ca-file contain overlapping certificates, --requestheader-allowed-names must be specified to ensure regular client certificates cannot set authenticating proxy headers for arbitrary users ([#131411](https://github.com/kubernetes/kubernetes/pull/131411), [@ballista01](https://github.com/ballista01)) [SIG API Machinery, Auth and Security] +- Kube-controller-manager: Resolves potential issues handling pods with incorrect uids in their ownerReference ([#134654](https://github.com/kubernetes/kubernetes/pull/134654), [@liggitt](https://github.com/liggitt)) [SIG Apps] +- 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. ([#134715](https://github.com/kubernetes/kubernetes/pull/134715), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: fixes a preflight check that can fail hostname construction in IPV6 setups ([#134588](https://github.com/kubernetes/kubernetes/pull/134588), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Auth, Cloud Provider, Cluster Lifecycle and Testing] +- Legacy watch calls (RV = 0 or unset) that generate init-events weigh higher in APF seat usage now. Properly accounting for their cost protects the API server from CPU overload. Users might see increased throttling of such calls as a result. ([#134601](https://github.com/kubernetes/kubernetes/pull/134601), [@shyamjvs](https://github.com/shyamjvs)) [SIG API Machinery] +- Prevent a segfault occurring when updating deeply nested JSON fields ([#134381](https://github.com/kubernetes/kubernetes/pull/134381), [@kon-angelo](https://github.com/kon-angelo)) [SIG API Machinery and CLI] +- The kubelet now honors the configuration userNamespaces.idsPerPod. Before it was ignored. ([#133373](https://github.com/kubernetes/kubernetes/pull/133373), [@AkihiroSuda](https://github.com/AkihiroSuda)) [SIG Node and Testing] + +### Other (Cleanup or Flake) + +- Building Kubernetes is now implemented by running a pre-built container image directly, without running rsyncd, and is substantially simplified. ([#134510](https://github.com/kubernetes/kubernetes/pull/134510), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release and Testing] +- CPU Manager static policy option `strict-cpu-reservation` moved to the GA version ([#134388](https://github.com/kubernetes/kubernetes/pull/134388), [@psasnal](https://github.com/psasnal)) [SIG Node] +- Dropped support for policy/v1beta1 PodDisruptionBudget in kubectl ([#134685](https://github.com/kubernetes/kubernetes/pull/134685), [@scaliby](https://github.com/scaliby)) [SIG CLI] +- Kubeadm: stoped applying the --pod-infra-container-image flag for the kubelet. The flag has been deprecated and no longer served a purpose in the kubelet as the logic was migrated to CRI. During upgrade, kubeadm will attempt to remove the flag from the file /var/lib/kubelet/kubeadm-flags.env. ([#133778](https://github.com/kubernetes/kubernetes/pull/133778), [@carlory](https://github.com/carlory)) [SIG Cloud Provider and Cluster Lifecycle] +- Kubeadm: updated the supported etcd version to v3.5.23 for supported control plane versions v1.31, v1.32, and v1.33. ([#134692](https://github.com/kubernetes/kubernetes/pull/134692), [@joshjms](https://github.com/joshjms)) [SIG Cluster Lifecycle and Etcd] +- Kubeadm: updated the supported etcd version to v3.5.24 for supported control plane versions v1.32, v1.33, and v1.34. ([#134779](https://github.com/kubernetes/kubernetes/pull/134779), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- Kubernetes is now built with go 1.25.3 ([#134598](https://github.com/kubernetes/kubernetes/pull/134598), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release] +- Promote the Topology Manager policy option max-allowable-numa-nodes to GA ([#134614](https://github.com/kubernetes/kubernetes/pull/134614), [@ffromani](https://github.com/ffromani)) [SIG Node] +- Rsync is no longer required to build kubernetes. ([#134656](https://github.com/kubernetes/kubernetes/pull/134656), [@BenTheElder](https://github.com/BenTheElder)) [SIG Release and Testing] +- The storage.k8s.io/v1alpha1 VolumeAttributesClass API is no longer served in 1.35 ([#134625](https://github.com/kubernetes/kubernetes/pull/134625), [@liggitt](https://github.com/liggitt)) [SIG API Machinery, Etcd, Storage and Testing] + +## Dependencies + +### Added +_Nothing has changed._ + +### Changed +_Nothing has changed._ + +### Removed +_Nothing has changed._ + + + +# v1.35.0-alpha.1 + + +## Downloads for v1.35.0-alpha.1 + + + +### Source Code + +filename | sha512 hash +-------- | ----------- +[kubernetes.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes.tar.gz) | 1d6fb6a4c7f82fe04e56757b733c3fc4aac652f8c2113e79ddce83b6cbe0179404147b35ddbc18e1b60eb802acb3f6d884599fd573f3d16f0558ef7ddfb8aae2 +[kubernetes-src.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-src.tar.gz) | 364788bac4d405ac6180fe3cb7e3d847e7960fcb0532146b105270aeac2624ade2ff87370c5aa8f768eda07fd28e5e75f73afbdf9cc1b786827a0e123bdea561 + +### Client Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-client-darwin-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-darwin-amd64.tar.gz) | 1ba40849b104851d922bce32dc9306004e9b95cfadeff9ecfb65f779892009f9a70878b8efe96159088b1ad8c700bf19e58d68416dfdff7853660e6074dd3752 +[kubernetes-client-darwin-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-darwin-arm64.tar.gz) | 0c939895ad2d53f57e9137774eed99cbfbfa5f15d4276f5f55c4ea40b922a5f37ab375a065fdd330f5a1ddf452896f2a13621075b049f382ab42a65ea1085dac +[kubernetes-client-linux-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-386.tar.gz) | e98a9b2d5f1c8bec552be6353b623a8f12078befd968a662a933907d0ff72b0164fa8b38e4cbd4aae6191e28aedc19d996ec99298053d7bafc458f91580a7cfa +[kubernetes-client-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-amd64.tar.gz) | 34ab1e9edf70c84fe58a223e91b0bf679e5d1273a2b6503a18a61a4bea79231948efe098f84e39f83dbfa2c5271aad8e9819aac104d6c3360c97e5c348b15be7 +[kubernetes-client-linux-arm.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-arm.tar.gz) | 554f8240597e7eb8470c8e2b4bca33c06a0a91746831ef93f76b344f5c3d6226d4ef26cb59f127d1436ac091b7b79395320fe8a9b2acc512afd601989c138d4a +[kubernetes-client-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-arm64.tar.gz) | 62c750654e898622aa87b2d81d4b0cbaf36614899f37181c2e3a6aa645d2270c4dedb7d6e7b974059a716ad144a5615407e6a9a4c03f761a512d37fdda796e50 +[kubernetes-client-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-ppc64le.tar.gz) | bbe22979c4e300675dfa955ce9855b7b33b29119a9f78e58bc1b088dba2b8dedbf0b068092d42cd98dbc10cda1da317eae6414b91587593678ec76780ff575df +[kubernetes-client-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-linux-s390x.tar.gz) | b8360d0bf930149d360da2a95549b35cb7e14932ae8507d99e34d93729ae645bab203dfba325c74db13204e09e5ee032f887cdd67badfbf3bc8a08d71ccf9c3d +[kubernetes-client-windows-386.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-windows-386.tar.gz) | cc935a74f30dcd1eaaeadc8f2353a9742ebc4a36b133342c6402b065750f4028a1a392bd5f7ea51533c2d799ff2bdb3d0f21493e7fabacb27289e58f011bb229 +[kubernetes-client-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-windows-amd64.tar.gz) | 119742103985be0cd4296b85aa2c713cdc510b9a9412706fdf88ca1c703f69338146efc5cf37168ab56e74576ad561ce37c3f500d29b63002139d11544b1b7cf +[kubernetes-client-windows-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-client-windows-arm64.tar.gz) | 67f5fe6b14aa4c49acb6f706ec4a9e43b87f1e19555579895452183e0b2d2be2202f8d48622208ac5ef6e0fb9050d99bb7e1ed9e4e31e8fcac7c0b5e44787c39 + +### Server Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-server-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-server-linux-amd64.tar.gz) | a1c935db625766b02113087068fa1087a6b74e9f57ad72cc1d5d85e830c0569b9257746013053ee8dc89404940458c3bba00064666978ddff4df9f3cae0ae066 +[kubernetes-server-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-server-linux-arm64.tar.gz) | 5f87f8f46719af413fa864b7d91d5b95fa86adf27df63cf904470b15e844ebda4c802d7ec7cb4006b4e5a3780903d0436eb57a7e2f2b79b74cf5e7e8f65496b1 +[kubernetes-server-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-server-linux-ppc64le.tar.gz) | 9f2e9476ae3b95919c991dd0438b31eeded7c7f686948ef2d6227311dabc952e74f61d3638f224eb18e63a4fe4955b55a9e358032477b989800541617a8b5f6a +[kubernetes-server-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-server-linux-s390x.tar.gz) | 043311de9dad81d3774decc3aeeac48833d2d234a15ce4a2062fd9af778879afde5e2eb9fdec5f3641725f630e7c3bd845a348ad713b54275e77293941d4c8d0 + +### Node Binaries + +filename | sha512 hash +-------- | ----------- +[kubernetes-node-linux-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-node-linux-amd64.tar.gz) | 865b4ee818cd53bc91001f658243e6b6fd9464f17ef8dc0cc739586689f39998e5c47630df439ad43553d6830ae3a7375cc780c3a5e49f1422d35d77194efc35 +[kubernetes-node-linux-arm64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-node-linux-arm64.tar.gz) | fc7df94bc328817d20c59e1ab1371634bf3849141ad982c9d403b136d009c3ed9ee3f7a20659a0f93af7179e13e2c0835b80fefc677b8840f7bcbde0dabb4483 +[kubernetes-node-linux-ppc64le.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-node-linux-ppc64le.tar.gz) | 8ec718436680766d9026b56ece5bff7a6bac9f63da0edf33843a7a6c255e1a1d22aecf260c1d5fc394c1e2f581931c5ae0acad80a0141fc8d4d7730bf04566b5 +[kubernetes-node-linux-s390x.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-node-linux-s390x.tar.gz) | fff0562e3a89b4f9444ce83bb8cfc860bcd5178a2c1ba0f1404d87556f483d48241aa3f927f2534d811b1c554958c167f1e5199fa916ebfd9a754cef2f761139 +[kubernetes-node-windows-amd64.tar.gz](https://dl.k8s.io/v1.35.0-alpha.1/kubernetes-node-windows-amd64.tar.gz) | 7774299a2b581a4ab2514d8dc95c1d0dff0652aa0518efbffa9ac39f5c510cb83d8a41a70e9b4e78f0b179e5a806402310dac161ff3a2398b97755938c225586 + +### 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.35.0-alpha.1](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.35.0-alpha.1](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.35.0-alpha.1](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.35.0-alpha.1](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.35.0-alpha.1](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.35.0-alpha.1](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.0 + +## Changes by Kind + +### API Change + +- Added WithOrigin within apis/core/validation with adjusted tests ([#132825](https://github.com/kubernetes/kubernetes/pull/132825), [@PatrickLaabs](https://github.com/PatrickLaabs)) [SIG Apps] +- Component-base: validate that log-flush-frequency is positive and return an error instead of panic-ing ([#133540](https://github.com/kubernetes/kubernetes/pull/133540), [@BenTheElder](https://github.com/BenTheElder)) [SIG Architecture, Instrumentation, Network and Node] +- Feature gate dependencies are now explicit, and validated at startup. A feature can no longer be enabled if it depends on a disabled feature. In particular, this means that `AllAlpha=true` will no longer work without enabling disabled-by-default beta features that are depended on (either with `AllBeta=true` or explicitly enumerating the disabled dependencies). ([#133697](https://github.com/kubernetes/kubernetes/pull/133697), [@tallclair](https://github.com/tallclair)) [SIG API Machinery, Architecture, Cluster Lifecycle and Node] +- In version 1.34, the PodObservedGenerationTracking feature has been upgraded to beta, and the description of the alpha version in the openapi has been removed. ([#133883](https://github.com/kubernetes/kubernetes/pull/133883), [@yangjunmyfm192085](https://github.com/yangjunmyfm192085)) [SIG Apps] +- Introduce a new declarative validation tag +k8s:customUnique to control listmap uniqueness ([#134279](https://github.com/kubernetes/kubernetes/pull/134279), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery and Auth] +- Kube-apiserver: Fixed a 1.34 regression in CustomResourceDefinition handling that incorrectly warned about unrecognized formats on number and integer properties ([#133896](https://github.com/kubernetes/kubernetes/pull/133896), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Contributor Experience, Network, Node and Scheduling] +- OpenAPI model packages of API types are generated into `zz_generated.model_name.go` files and are accessible using the `OpenAPIModelName()` function. This allows API authors to declare the desired OpenAPI model packages instead of using the go package path of API types. ([#131755](https://github.com/kubernetes/kubernetes/pull/131755), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery, Apps, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- Support for `kubectl get -o kyaml` is now on by default. To disable it, set `KUBECTL_KYAML=false`. ([#133327](https://github.com/kubernetes/kubernetes/pull/133327), [@thockin](https://github.com/thockin)) [SIG CLI] +- The storage version for MutatingAdmissionPolicy is updated to v1beta1. ([#133715](https://github.com/kubernetes/kubernetes/pull/133715), [@cici37](https://github.com/cici37)) [SIG API Machinery, Etcd and Testing] + +### Feature + +- Add paths section to kubelet statusz endpoint ([#133239](https://github.com/kubernetes/kubernetes/pull/133239), [@Peac36](https://github.com/Peac36)) [SIG Node] +- Add paths section to scheduler statusz endpoint ([#132606](https://github.com/kubernetes/kubernetes/pull/132606), [@Peac36](https://github.com/Peac36)) [SIG API Machinery, Architecture, Instrumentation, Network, Node, Scheduling and Testing] +- Added kubectl config set-context -n flag as a shorthand for --namespace ([#134384](https://github.com/kubernetes/kubernetes/pull/134384), [@tchap](https://github.com/tchap)) [SIG CLI and Testing] +- Added remote runtime and image `Close()` method to be able to close the connection. ([#133211](https://github.com/kubernetes/kubernetes/pull/133211), [@saschagrunert](https://github.com/saschagrunert)) [SIG Node] +- Adds metric for Maxunavailable feature ([#130951](https://github.com/kubernetes/kubernetes/pull/130951), [@Edwinhr716](https://github.com/Edwinhr716)) [SIG Apps and Instrumentation] +- Applyconfiguration-gen now generates extract functions for all subresources ([#132665](https://github.com/kubernetes/kubernetes/pull/132665), [@mrIncompetent](https://github.com/mrIncompetent)) [SIG API Machinery] +- Applyconfiguration-gen now preserves struct and field comments from source types in generated code ([#132663](https://github.com/kubernetes/kubernetes/pull/132663), [@mrIncompetent](https://github.com/mrIncompetent)) [SIG API Machinery] +- DRA: the resource.k8s.io API now uses the v1 API version (introduced in 1.34) as default storage version. Downgrading to 1.33 is not supported. ([#133876](https://github.com/kubernetes/kubernetes/pull/133876), [@kei01234kei](https://github.com/kei01234kei)) [SIG API Machinery, Etcd and Testing] +- Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Warning Failed 7m11s (x2 over 7m33s) kubelet spec.containers{nginx}: Failed to pull image "nginx": failed to pull and unpack image... ([#133627](https://github.com/kubernetes/kubernetes/pull/133627), [@itzPranshul](https://github.com/itzPranshul)) [SIG CLI] +- Introduces e2e tests that check component invariant metrics across the entire suite run. ([#133394](https://github.com/kubernetes/kubernetes/pull/133394), [@BenTheElder](https://github.com/BenTheElder)) [SIG Testing] +- K8s.io/apimachinery: Introduce a helper function to compare resourceVersion strings from two objects of the same resource ([#134330](https://github.com/kubernetes/kubernetes/pull/134330), [@michaelasp](https://github.com/michaelasp)) [SIG API Machinery, Apps, Auth, Instrumentation, Network, Node, Scheduling, Storage and Testing] +- Kubeadm: graduate the kubeadm specific feature gate ControlPlaneKubeletLocalMode to GA and lock it to enabled by default. To opt-out manually from this desired default behavior you must patch the "server" field in the /etc/kubernetes/kubelet.conf file. The subphase of "kubeadm join phase control-plane-join" called "etcd" is now deprecated, hidden and replaced by the subphase with identical functionality "etcd-join". "etcd" will be removed in a follow-up release. The subphase "kubelet-wait-bootstrap" of "kubeadm join" is no longer experimental and will always run. ([#134106](https://github.com/kubernetes/kubernetes/pull/134106), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubernetes is now built using Go 1.25.1 ([#134095](https://github.com/kubernetes/kubernetes/pull/134095), [@dims](https://github.com/dims)) [SIG Release and Testing] +- Kubernetes now uses Go Language Version 1.25, including https://go.dev/blog/container-aware-gomaxprocs ([#134120](https://github.com/kubernetes/kubernetes/pull/134120), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture, Auth, CLI, Cloud Provider, Cluster Lifecycle, Instrumentation, Network, Node, Release, Scheduling and Storage] +- Lock down the `AllowOverwriteTerminationGracePeriodSeconds` feature gate. ([#133792](https://github.com/kubernetes/kubernetes/pull/133792), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node] +- Metrics: exclude dryRun requests from apiserver_request_sli_duration_seconds ([#131092](https://github.com/kubernetes/kubernetes/pull/131092), [@aldudko](https://github.com/aldudko)) [SIG API Machinery and Instrumentation] +- The validation in the resouce.k8s.io has been migrated to declarative validation. + If the `DeclarativeValidation` feature gate is enabled, mismatches with existing validation are reported via metrics. + If the `DeclarativeValidationTakeover` feature gate is enabled, declarative validation is the primary source of errors for migrated fields. ([#134072](https://github.com/kubernetes/kubernetes/pull/134072), [@yongruilin](https://github.com/yongruilin)) [SIG API Machinery, Apps and Auth] + +### Bug or Regression + +- Added the correct error when eviction is blocked due to the failSafe mechanism of the DisruptionController. ([#133097](https://github.com/kubernetes/kubernetes/pull/133097), [@kei01234kei](https://github.com/kei01234kei)) [SIG Apps and Node] +- Bugfix: the default serviceCIDR controller was not logging events because the event broadcaster was shutdown during its initialization. ([#133338](https://github.com/kubernetes/kubernetes/pull/133338), [@aojea](https://github.com/aojea)) [SIG Network] +- Deprecated metrics will be hidden as per the metrics deprecation policy https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecating-a-metric ([#133436](https://github.com/kubernetes/kubernetes/pull/133436), [@richabanker](https://github.com/richabanker)) [SIG Architecture, Instrumentation and Network] +- Fix incorrect behavior of preemptor pod when preemption of the victim takes long to complete. The preemptor pod should not be circling in scheduling cycles until preemption is finished. ([#134294](https://github.com/kubernetes/kubernetes/pull/134294), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Scheduling and Testing] +- Fix missing kubelet_volume_stats_* metrics ([#133890](https://github.com/kubernetes/kubernetes/pull/133890), [@huww98](https://github.com/huww98)) [SIG Instrumentation and Node] +- Fix occasional schedule delay when the static PV is created ([#133929](https://github.com/kubernetes/kubernetes/pull/133929), [@huww98](https://github.com/huww98)) [SIG Scheduling and Storage] +- Fix resource claims deallocation for extended resource when pod is completed ([#134312](https://github.com/kubernetes/kubernetes/pull/134312), [@alaypatel07](https://github.com/alaypatel07)) [SIG Apps, Node and Testing] +- Fixed SELinux warning controller not emitting events on some SELinux label conflicts. ([#133425](https://github.com/kubernetes/kubernetes/pull/133425), [@jsafrane](https://github.com/jsafrane)) [SIG Apps, Storage and Testing] +- 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. ([#134024](https://github.com/kubernetes/kubernetes/pull/134024), [@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. ([#134245](https://github.com/kubernetes/kubernetes/pull/134245), [@macsko](https://github.com/macsko)) [SIG Scheduling and Testing] +- Fixed a bug that caused apiservers to send an inappropriate Content-Type request header to authorization, token authentication, imagepolicy admission, and audit webhooks when the alpha client-go feature gate "ClientsPreferCBOR" is enabled. ([#132960](https://github.com/kubernetes/kubernetes/pull/132960), [@benluddy](https://github.com/benluddy)) [SIG API Machinery and Node] +- Fixed a bug that caused duplicate validation when updating PersistentVolumeClaims, VolumeAttachments and VolumeAttributesClasses. ([#132549](https://github.com/kubernetes/kubernetes/pull/132549), [@gavinkflam](https://github.com/gavinkflam)) [SIG Storage] +- Fixed a bug that caused duplicate validation when updating role and role binding resources. ([#132550](https://github.com/kubernetes/kubernetes/pull/132550), [@gavinkflam](https://github.com/gavinkflam)) [SIG Auth] +- Fixed a bug where high latency kube-apiserver caused scheduling throughput degradation. ([#134154](https://github.com/kubernetes/kubernetes/pull/134154), [@macsko](https://github.com/macsko)) [SIG Scheduling] +- Fixed broken shell completion for api resources. ([#133771](https://github.com/kubernetes/kubernetes/pull/133771), [@marckhouzam](https://github.com/marckhouzam)) [SIG CLI] +- Fixed validation error when ConfigFlags has CertFile and (or) KeyFile and original config also contains CertFileData and (or) KeyFileData. ([#133917](https://github.com/kubernetes/kubernetes/pull/133917), [@n2h9](https://github.com/n2h9)) [SIG API Machinery and CLI] +- Fixes a possible data race during metrics registration ([#134390](https://github.com/kubernetes/kubernetes/pull/134390), [@liggitt](https://github.com/liggitt)) [SIG Architecture and Instrumentation] +- Implicit extended resource name derived from device class (deviceclass.resource.kubernetes.io/) can be used to request DRA devices matching the device class. ([#133363](https://github.com/kubernetes/kubernetes/pull/133363), [@yliaog](https://github.com/yliaog)) [SIG Node, Scheduling and Testing] +- Kube-apiserver: Fixes a 1.34 regression with spurious "Error getting keys" log messages ([#133817](https://github.com/kubernetes/kubernetes/pull/133817), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] +- Kube-apiserver: Fixes a possible 1.34 performance regression calculating object size statistics for resources not served from the watch cache, typically only Events ([#133873](https://github.com/kubernetes/kubernetes/pull/133873), [@serathius](https://github.com/serathius)) [SIG API Machinery and Etcd] +- Kube-apiserver: improve the validation error message shown for custom resources with CEL validation rules to include the value that failed validation ([#132798](https://github.com/kubernetes/kubernetes/pull/132798), [@cbandy](https://github.com/cbandy)) [SIG API Machinery] +- Kube-controller-manager: Fixes a possible data race in the garbage collection controller ([#134379](https://github.com/kubernetes/kubernetes/pull/134379), [@liggitt](https://github.com/liggitt)) [SIG API Machinery and Apps] +- 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. ([#134265](https://github.com/kubernetes/kubernetes/pull/134265), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: fix KUBEADM_UPGRADE_DRYRUN_DIR not honored in upgrade phase when writing kubelet config files ([#134007](https://github.com/kubernetes/kubernetes/pull/134007), [@carlory](https://github.com/carlory)) [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. ([#134319](https://github.com/kubernetes/kubernetes/pull/134319), [@neolit123](https://github.com/neolit123)) [SIG Cluster Lifecycle] +- Kubeadm: fixed bug where v1beta3's ClusterConfiguration.APIServer.TimeoutForControlPlane is not respected in newer versions of kubeadm where v1beta4 is the default. ([#133513](https://github.com/kubernetes/kubernetes/pull/133513), [@tom1299](https://github.com/tom1299)) [SIG Cluster Lifecycle] +- Kubelet: the connection to a DRA driver became unusable because of an internal deadlock when a connection was idle for 30 minutes. ([#133926](https://github.com/kubernetes/kubernetes/pull/133926), [@pohly](https://github.com/pohly)) [SIG Node] +- Pod can have multiple volumes reference the same PVC ([#122140](https://github.com/kubernetes/kubernetes/pull/122140), [@huww98](https://github.com/huww98)) [SIG Node, Storage and Testing] +- Previously, `kubectl scale` returned the error message `error: no objects passed to scale "" not found` when the specified resource did not exist. + For consistency with other commands(e.g. `kubectl get`), it has been changed to just return `Error from server (NotFound): "" not found`. ([#134017](https://github.com/kubernetes/kubernetes/pull/134017), [@mochizuki875](https://github.com/mochizuki875)) [SIG CLI] +- Promote VAC API test to conformance ([#133615](https://github.com/kubernetes/kubernetes/pull/133615), [@carlory](https://github.com/carlory)) [SIG Architecture, Storage and Testing] +- Remove incorrectly printed warning for SessionAffinity whenever a headless service is creater or updated ([#134054](https://github.com/kubernetes/kubernetes/pull/134054), [@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. ([#134400](https://github.com/kubernetes/kubernetes/pull/134400), [@macsko](https://github.com/macsko)) [SIG Scheduling] +- When image garbage collection is unable to free enough disk space, the FreeDiskSpaceFailed warning event is now more actionable. Example: `Insufficient free disk space on the node's image filesystem (95.0% of 10.0 GiB used). Failed to free sufficient space by deleting unused images. Consider resizing the disk or deleting unused files.` ([#132578](https://github.com/kubernetes/kubernetes/pull/132578), [@drigz](https://github.com/drigz)) [SIG Node] + +### Other (Cleanup or Flake) + +- Bump addon manager to use kubectl v1.32.2 ([#130548](https://github.com/kubernetes/kubernetes/pull/130548), [@Jefftree](https://github.com/Jefftree)) [SIG Cloud Provider, Scalability and Testing] +- Dropping the experimental prefix from kubectl wait command's short description, since kubectl wait command has been stable for a long time. ([#133907](https://github.com/kubernetes/kubernetes/pull/133907), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] +- Fix formatting of assorted go API deprecations for godoc / pkgsite and enable a linter to help catch mis-formatted deprecations ([#133571](https://github.com/kubernetes/kubernetes/pull/133571), [@BenTheElder](https://github.com/BenTheElder)) [SIG API Machinery, Architecture, CLI, Instrumentation and Testing] +- Improved HPA performance when using container-specific resource metrics by optimizing container lookup logic to exit early once the target container is found, reducing unnecessary iterations through all containers in a pod. ([#133415](https://github.com/kubernetes/kubernetes/pull/133415), [@AadiDev005](https://github.com/AadiDev005)) [SIG Apps and Autoscaling] +- Kube-apiserver: Fixes an issue where passing invalid DeleteOptions incorrectly returned status 500 rather than 400. ([#133358](https://github.com/kubernetes/kubernetes/pull/133358), [@ostrain](https://github.com/ostrain)) [SIG API Machinery] +- Kubeadm: removed the `RootlessControlPlane` feature gate. User Namespaces will serve as its replacement. ([#134178](https://github.com/kubernetes/kubernetes/pull/134178), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Cluster Lifecycle] +- Remove container name from messages for container created and started events. ([#134043](https://github.com/kubernetes/kubernetes/pull/134043), [@HirazawaUi](https://github.com/HirazawaUi)) [SIG Node] +- Removed deprecated gogo protocol definitions from `k8s.io/kubelet/pkg/apis/dra` in favor of `google.golang.org/protobuf`. ([#133026](https://github.com/kubernetes/kubernetes/pull/133026), [@saschagrunert](https://github.com/saschagrunert)) [SIG API Machinery and Node] +- Removed general available feature-gate SizeMemoryBackedVolumes ([#133720](https://github.com/kubernetes/kubernetes/pull/133720), [@carlory](https://github.com/carlory)) [SIG Node, Storage and Testing] +- Removed the `ComponentSLIs` feature gate, which had been promoted to stable as part of the Kubernetes 1.32 release. ([#133742](https://github.com/kubernetes/kubernetes/pull/133742), [@carlory](https://github.com/carlory)) [SIG Architecture and Instrumentation] +- Removing Experimental prefix from the description of kubectl wait to emphasize that it is stable. ([#133731](https://github.com/kubernetes/kubernetes/pull/133731), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] +- Removing the KUBECTL_OPENAPIV3_PATCH environment variable entirely, since aggregated discovery has been stable from 1.30. ([#134130](https://github.com/kubernetes/kubernetes/pull/134130), [@ardaguclu](https://github.com/ardaguclu)) [SIG CLI] +- Specifies the deprecated version of apiserver_storage_objects metric in metrics docs ([#134028](https://github.com/kubernetes/kubernetes/pull/134028), [@richabanker](https://github.com/richabanker)) [SIG API Machinery, Etcd and Instrumentation] +- Tests: switch to https://go.dev/doc/go1.25#container-aware-gomaxprocs from go.uber.org/automaxprocs ([#133492](https://github.com/kubernetes/kubernetes/pull/133492), [@BenTheElder](https://github.com/BenTheElder)) [SIG Testing] +- The `/statusz` page for `kube-proxy` now includes a list of exposed endpoints, making it easier to debug and introspect. ([#133190](https://github.com/kubernetes/kubernetes/pull/133190), [@aman4433](https://github.com/aman4433)) [SIG Network and Node] +- Types in k/k/pkg/scheduler/framework: + Handle, + Plugin, + PreEnqueuePlugin, QueueSortPlugin, EnqueueExtensions, PreFilterExtensions, PreFilterPlugin, FilterPlugin, PostFilterPlugin, PreScorePlugin, ScorePlugin, ReservePlugin, PreBindPlugin, PostBindPlugin, PermitPlugin, BindPlugin, + PodActivator, PodNominator, PluginsRunner, + LessFunc, ScoreExtensions, NodeToStatusReader, NodeScoreList, NodeScore, NodePluginScores, PluginScore, NominatingMode, NominatingInfo, WaitingPod, PreFilterResult, PostFilterResult, + Extender, + NodeInfoLister, StorageInfoLister, SharedLister, ResourceSliceLister, DeviceClassLister, ResourceClaimTracker, SharedDRAManager + + are moved to package k8s.io/kube-scheduler/framework . Users should update import paths. The interfaces don't change. + + Type Parallelizer in k/k/pkg/scheduler/framework/parallelism is split into interface Parallelizer (in k8s.io/kube-scheduler/framework) and struct Parallelizer (location unchanged in k/k). Plugin developers should update the import path to staging repo. ([#133172](https://github.com/kubernetes/kubernetes/pull/133172), [@ania-borowiec](https://github.com/ania-borowiec)) [SIG Node, Release, Scheduling, Storage and Testing] +- Updated CNI plugins to v1.8.0. ([#133837](https://github.com/kubernetes/kubernetes/pull/133837), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider, Node and Testing] +- Updated cri-tools to v1.34.0. ([#133636](https://github.com/kubernetes/kubernetes/pull/133636), [@saschagrunert](https://github.com/saschagrunert)) [SIG Cloud Provider] +- Updated etcd to v3.6.5. ([#134251](https://github.com/kubernetes/kubernetes/pull/134251), [@joshjms](https://github.com/joshjms)) [SIG API Machinery, Cloud Provider, Cluster Lifecycle, Etcd and Testing] +- Upgrade CoreDNS to v1.12.3 ([#132288](https://github.com/kubernetes/kubernetes/pull/132288), [@thevilledev](https://github.com/thevilledev)) [SIG Cloud Provider and Cluster Lifecycle] +- `kubectl auth reconcile` now re-attempts reconciliation if it encounters a conflict error ([#133323](https://github.com/kubernetes/kubernetes/pull/133323), [@liggitt](https://github.com/liggitt)) [SIG Auth and CLI] +- `kubectl get` and `kubectl describe` human-readable output no longer includes counts for referenced tokens and secrets ([#117160](https://github.com/kubernetes/kubernetes/pull/117160), [@liggitt](https://github.com/liggitt)) [SIG CLI and Testing] + +## Dependencies + +### Added +- github.com/moby/sys/atomicwriter: [v0.1.0](https://github.com/moby/sys/tree/atomicwriter/v0.1.0) +- golang.org/x/tools/go/expect: v0.1.1-deprecated +- golang.org/x/tools/go/packages/packagestest: v0.1.1-deprecated + +### Changed +- cloud.google.com/go/compute/metadata: v0.6.0 → v0.7.0 +- github.com/aws/aws-sdk-go-v2/config: [v1.27.24 → v1.29.14](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.24...config/v1.29.14) +- github.com/aws/aws-sdk-go-v2/credentials: [v1.17.24 → v1.17.67](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.17.24...credentials/v1.17.67) +- github.com/aws/aws-sdk-go-v2/feature/ec2/imds: [v1.16.9 → v1.16.30](https://github.com/aws/aws-sdk-go-v2/compare/feature/ec2/imds/v1.16.9...feature/ec2/imds/v1.16.30) +- github.com/aws/aws-sdk-go-v2/internal/configsources: [v1.3.13 → v1.3.34](https://github.com/aws/aws-sdk-go-v2/compare/internal/configsources/v1.3.13...internal/configsources/v1.3.34) +- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2: [v2.6.13 → v2.6.34](https://github.com/aws/aws-sdk-go-v2/compare/internal/endpoints/v2/v2.6.13...internal/endpoints/v2/v2.6.34) +- github.com/aws/aws-sdk-go-v2/internal/ini: [v1.8.0 → v1.8.3](https://github.com/aws/aws-sdk-go-v2/compare/internal/ini/v1.8.0...internal/ini/v1.8.3) +- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding: [v1.11.3 → v1.12.3](https://github.com/aws/aws-sdk-go-v2/compare/service/internal/accept-encoding/v1.11.3...service/internal/accept-encoding/v1.12.3) +- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url: [v1.11.15 → v1.12.15](https://github.com/aws/aws-sdk-go-v2/compare/service/internal/presigned-url/v1.11.15...service/internal/presigned-url/v1.12.15) +- github.com/aws/aws-sdk-go-v2/service/sso: [v1.22.1 → v1.25.3](https://github.com/aws/aws-sdk-go-v2/compare/service/sso/v1.22.1...service/sso/v1.25.3) +- github.com/aws/aws-sdk-go-v2/service/ssooidc: [v1.26.2 → v1.30.1](https://github.com/aws/aws-sdk-go-v2/compare/service/ssooidc/v1.26.2...service/ssooidc/v1.30.1) +- github.com/aws/aws-sdk-go-v2/service/sts: [v1.30.1 → v1.33.19](https://github.com/aws/aws-sdk-go-v2/compare/service/sts/v1.30.1...service/sts/v1.33.19) +- github.com/aws/aws-sdk-go-v2: [v1.30.1 → v1.36.3](https://github.com/aws/aws-sdk-go-v2/compare/v1.30.1...v1.36.3) +- github.com/aws/smithy-go: [v1.20.3 → v1.22.3](https://github.com/aws/smithy-go/compare/v1.20.3...v1.22.3) +- github.com/containerd/containerd/api: [v1.8.0 → v1.9.0](https://github.com/containerd/containerd/compare/api/v1.8.0...api/v1.9.0) +- github.com/containerd/ttrpc: [v1.2.6 → v1.2.7](https://github.com/containerd/ttrpc/compare/v1.2.6...v1.2.7) +- github.com/containerd/typeurl/v2: [v2.2.2 → v2.2.3](https://github.com/containerd/typeurl/compare/v2.2.2...v2.2.3) +- github.com/coredns/corefile-migration: [v1.0.26 → v1.0.27](https://github.com/coredns/corefile-migration/compare/v1.0.26...v1.0.27) +- github.com/docker/docker: [v26.1.4+incompatible → v28.2.2+incompatible](https://github.com/docker/docker/compare/v26.1.4...v28.2.2) +- github.com/go-logr/logr: [v1.4.2 → v1.4.3](https://github.com/go-logr/logr/compare/v1.4.2...v1.4.3) +- github.com/google/cadvisor: [v0.52.1 → v0.53.0](https://github.com/google/cadvisor/compare/v0.52.1...v0.53.0) +- github.com/opencontainers/cgroups: [v0.0.1 → v0.0.3](https://github.com/opencontainers/cgroups/compare/v0.0.1...v0.0.3) +- github.com/opencontainers/runc: [v1.2.5 → v1.3.0](https://github.com/opencontainers/runc/compare/v1.2.5...v1.3.0) +- github.com/opencontainers/runtime-spec: [v1.2.0 → v1.2.1](https://github.com/opencontainers/runtime-spec/compare/v1.2.0...v1.2.1) +- github.com/prometheus/client_golang: [v1.22.0 → v1.23.2](https://github.com/prometheus/client_golang/compare/v1.22.0...v1.23.2) +- github.com/prometheus/client_model: [v0.6.1 → v0.6.2](https://github.com/prometheus/client_model/compare/v0.6.1...v0.6.2) +- github.com/prometheus/common: [v0.62.0 → v0.66.1](https://github.com/prometheus/common/compare/v0.62.0...v0.66.1) +- github.com/prometheus/procfs: [v0.15.1 → v0.16.1](https://github.com/prometheus/procfs/compare/v0.15.1...v0.16.1) +- github.com/spf13/cobra: [v1.9.1 → v1.10.0](https://github.com/spf13/cobra/compare/v1.9.1...v1.10.0) +- github.com/spf13/pflag: [v1.0.6 → v1.0.9](https://github.com/spf13/pflag/compare/v1.0.6...v1.0.9) +- github.com/stretchr/testify: [v1.10.0 → v1.11.1](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1) +- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp: v0.58.0 → v0.61.0 +- go.opentelemetry.io/otel/metric: v1.35.0 → v1.36.0 +- go.opentelemetry.io/otel/sdk/metric: v1.34.0 → v1.36.0 +- go.opentelemetry.io/otel/sdk: v1.34.0 → v1.36.0 +- go.opentelemetry.io/otel/trace: v1.35.0 → v1.36.0 +- go.opentelemetry.io/otel: v1.35.0 → v1.36.0 +- golang.org/x/crypto: v0.36.0 → v0.41.0 +- golang.org/x/mod: v0.21.0 → v0.27.0 +- golang.org/x/net: v0.38.0 → v0.43.0 +- golang.org/x/oauth2: v0.27.0 → v0.30.0 +- golang.org/x/sync: v0.12.0 → v0.16.0 +- golang.org/x/sys: v0.31.0 → v0.35.0 +- golang.org/x/telemetry: bda5523 → 1a19826 +- golang.org/x/term: v0.30.0 → v0.34.0 +- golang.org/x/text: v0.23.0 → v0.28.0 +- golang.org/x/tools: v0.26.0 → v0.36.0 +- google.golang.org/genproto/googleapis/rpc: a0af3ef → 200df99 +- google.golang.org/grpc: v1.72.1 → v1.72.2 +- google.golang.org/protobuf: v1.36.5 → v1.36.8 +- gopkg.in/evanphx/json-patch.v4: v4.12.0 → v4.13.0 +- k8s.io/gengo/v2: 85fd79d → ec3ebc5 +- k8s.io/kube-openapi: f3f2b99 → 589584f +- k8s.io/system-validators: v1.10.1 → v1.11.1 +- sigs.k8s.io/json: cfa47c3 → 2d32026 + +### Removed +- gopkg.in/yaml.v2: v2.4.0 \ No newline at end of file diff --git a/CHANGELOG/README.md b/CHANGELOG/README.md index b4a7e01f97a69..c587a652174a0 100644 --- a/CHANGELOG/README.md +++ b/CHANGELOG/README.md @@ -1,5 +1,6 @@ # CHANGELOGs +- [CHANGELOG-1.35.md](./CHANGELOG-1.35.md) - [CHANGELOG-1.34.md](./CHANGELOG-1.34.md) - [CHANGELOG-1.33.md](./CHANGELOG-1.33.md) - [CHANGELOG-1.32.md](./CHANGELOG-1.32.md) diff --git a/LICENSES/vendor/cyphar.com/go-pathrs/LICENSE b/LICENSES/vendor/cyphar.com/go-pathrs/LICENSE new file mode 100644 index 0000000000000..d0fe550bcdf4e --- /dev/null +++ b/LICENSES/vendor/cyphar.com/go-pathrs/LICENSE @@ -0,0 +1,377 @@ += vendor/cyphar.com/go-pathrs licensed under: = + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + += vendor/cyphar.com/go-pathrs/COPYING f75d2927d3c1ed2414ef72048f5ad640 diff --git a/LICENSES/vendor/github.com/Masterminds/semver/v3/LICENSE b/LICENSES/vendor/github.com/Masterminds/semver/v3/LICENSE new file mode 100644 index 0000000000000..6c8aec15ac15c --- /dev/null +++ b/LICENSES/vendor/github.com/Masterminds/semver/v3/LICENSE @@ -0,0 +1,23 @@ += vendor/github.com/Masterminds/semver/v3 licensed under: = + +Copyright (C) 2014-2019, Matt Butcher and Matt Farina + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + += vendor/github.com/Masterminds/semver/v3/LICENSE.txt 4c1ffeeb02e3f8f4af042205e189b3f7 diff --git a/LICENSES/vendor/github.com/cyphar/filepath-securejoin/LICENSE b/LICENSES/vendor/github.com/cyphar/filepath-securejoin/LICENSE index 192e9c6239076..ebcf32a6f5680 100644 --- a/LICENSES/vendor/github.com/cyphar/filepath-securejoin/LICENSE +++ b/LICENSES/vendor/github.com/cyphar/filepath-securejoin/LICENSE @@ -29,4 +29,4 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -= vendor/github.com/cyphar/filepath-securejoin/LICENSE 7e05df0b39896d74600ef94ab46dce89 += vendor/github.com/cyphar/filepath-securejoin/LICENSE.BSD 7e05df0b39896d74600ef94ab46dce89 diff --git a/LICENSES/vendor/github.com/opencontainers/runc/LICENSE b/LICENSES/vendor/github.com/opencontainers/runc/LICENSE deleted file mode 100644 index dac7dbf7e97cd..0000000000000 --- a/LICENSES/vendor/github.com/opencontainers/runc/LICENSE +++ /dev/null @@ -1,195 +0,0 @@ -= vendor/github.com/opencontainers/runc licensed under: = - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2014 Docker, Inc. - - 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. - -= vendor/github.com/opencontainers/runc/LICENSE 435b266b3899aa8a959f17d41c56def8 diff --git a/OWNERS b/OWNERS index 905df925d42d2..00ca8b940ed3f 100644 --- a/OWNERS +++ b/OWNERS @@ -18,9 +18,9 @@ filters: - smarterclayton - thockin - wojtek-t - # go.{mod,sum} files relate to go dependencies, and should be reviewed by the - # dep-approvers - "go\\.(mod|sum)$": + # go.{mod,sum,work,work.sum} files relate to go dependencies, + # and should be reviewed by the dep-approvers + "go\\.(mod|sum|work|work\\.sum)$": approvers: - dep-approvers reviewers: diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index d3166c3fadbc4..b4832c3fcb67f 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -1,4 +1,12 @@ aliases: + sig-api-machinery-approvers: + - deads2k + - jpbetz + - sttts + sig-api-machinery-reviewers: + - deads2k + - jpbetz + - sttts # Note: sig-architecture-approvers has approval on root files (including go.mod/go.sum) until https://github.com/kubernetes/test-infra/pull/21398 is resolved. # People with approve rights via this alias should defer dependency update PRs to dep-approvers. sig-architecture-approvers: @@ -179,6 +187,7 @@ aliases: - dom4ha - macsko - sanposhiho + - utam0k - kerthcet # emeritus: # - adtac @@ -249,6 +258,7 @@ aliases: - dims - endocrimes - feiskyer + - HirazawaUi - mtaufen - sjenning - wzshiming @@ -269,6 +279,7 @@ aliases: - kannon92 - ffromani - tallclair + - natasha41575 sig-network-approvers: - aojea - bowei @@ -310,10 +321,14 @@ aliases: # - tnozicka sig-autoscaling-maintainers: + - adrianmoisey - gjtempleton - jackfrancis - - raywainman - towca + # emeritus + # - maciekpytel + # - mwielgus + # - raywainman sig-instrumentation-approvers: - logicalhan - dashpole @@ -460,11 +475,22 @@ aliases: - MadhavJivrajani - mfahlandt - Priyankasaggu11929 + # See https://github.com/kubernetes/org/blob/main/OWNERS_ALIASES + # for the authoritative list of SIG Docs leads, or look for + # https://github.com/kubernetes/website/blob/main/OWNERS_ALIASES + # for the list that this was derived from. SIG Docs' leads can + # confirm modifications here. sig-docs-approvers: - - jimangel - - kbhawkey - - onlydole - - sftim + - dipesh-rawat + - divya-mohan0209 + - katcosgrove + - lmktfy + - natalisucks + - nate-double-u + - reylejano + - salaxander + - SayakMukhopadhyay + - tengqm sig-node-api-reviewers: - dchen1107 - derekwaynecarr @@ -512,8 +538,8 @@ aliases: dep-reviewers: - logicalhan feature-approvers: + - adrianmoisey # Autoscaling - andrewsykim # Cloud Provider - - ahg-g # Scheduling - ardaguclu # CLI - aojea # Network, Testing - danwinship # Network @@ -523,9 +549,11 @@ aliases: - derekwaynecarr # Node - dgrisonnet # Instrumentation - dims # Architecture + - dom4ha # Scheduling - eddiezane # CLI - enj # Auth - gjtempleton # Autoscaling + - jackfrancis # Autoscaling - janetkuo # Apps - jayunit100 # Windows - jpbetz # API Machinery @@ -536,7 +564,7 @@ aliases: - liggitt # Auth - logicalhan # Instrumentation - luxas # Cluster Lifecycle - - maciekpytel # Autoscaling + - macsko # Scheduling - marosset # Windows - mikezappa87 # Network - mrunalp # Node @@ -544,12 +572,14 @@ aliases: - rexagod # Instrumentation - richabanker # Instrumentation - saad-ali # Storage + - sanposhiho # Scheduling - sergeykanzhelev # Node - shaneutt # Network - soltysh # Apps, CLI - sttts # API Machinery - tallclair # Auth - thockin # Network + - towca # Autoscaling - xing-yang # Storage - wojtek-t # Scalability # conformance aliases https://git.k8s.io/enhancements/keps/sig-architecture/20190412-conformance-behaviors.md diff --git a/api/api-rules/aggregator_violation_exceptions.list b/api/api-rules/aggregator_violation_exceptions.list index 8f1c9408a636a..403bff328f00c 100644 --- a/api/api-rules/aggregator_violation_exceptions.list +++ b/api/api-rules/aggregator_violation_exceptions.list @@ -1,3 +1,9 @@ +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,Format +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,d +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,i +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,s +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,scale +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,value API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEvent,Object diff --git a/api/api-rules/apiextensions_violation_exceptions.list b/api/api-rules/apiextensions_violation_exceptions.list index 9f9227be5ce0e..34b860195553b 100644 --- a/api/api-rules/apiextensions_violation_exceptions.list +++ b/api/api-rules/apiextensions_violation_exceptions.list @@ -29,6 +29,12 @@ API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiexten API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrBool,Schema API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrStringArray,Property API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrStringArray,Schema +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,Format +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,d +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,i +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,s +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,scale +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,value API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEvent,Object diff --git a/api/api-rules/codegen_violation_exceptions.list b/api/api-rules/codegen_violation_exceptions.list index 8f1c9408a636a..eeee5ba290fa3 100644 --- a/api/api-rules/codegen_violation_exceptions.list +++ b/api/api-rules/codegen_violation_exceptions.list @@ -1,3 +1,9 @@ +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,Format +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,d +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,i +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,s +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,scale +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,value API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEvent,Object @@ -7,3 +13,5 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,StatusCause API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Time,Time API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,ContentEncoding API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,ContentType +API rule violation: names_match,k8s.io/code-generator/examples/apiserver/apis/example/v1,ConversionCustom,privateField +API rule violation: names_match,k8s.io/code-generator/examples/apiserver/apis/example/v1,ConversionPrivate,privateField diff --git a/api/api-rules/sample_apiserver_violation_exceptions.list b/api/api-rules/sample_apiserver_violation_exceptions.list index 8f1c9408a636a..403bff328f00c 100644 --- a/api/api-rules/sample_apiserver_violation_exceptions.list +++ b/api/api-rules/sample_apiserver_violation_exceptions.list @@ -1,3 +1,9 @@ +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,Format +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,d +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,i +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,Quantity,s +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,scale +API rule violation: names_match,k8s.io/apimachinery/pkg/api/resource,int64Amount,value API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEvent,Object diff --git a/api/api-rules/violation_exceptions.list b/api/api-rules/violation_exceptions.list index 61824bff581ff..6d16fc9fda746 100644 --- a/api/api-rules/violation_exceptions.list +++ b/api/api-rules/violation_exceptions.list @@ -1,4 +1,3 @@ -API rule violation: list_type_missing,k8s.io/api/storagemigration/v1alpha1,StorageVersionMigrationList,Items API rule violation: list_type_missing,k8s.io/cloud-provider/config/v1alpha1,WebhookConfiguration,Webhooks API rule violation: list_type_missing,k8s.io/controller-manager/config/v1alpha1,GenericControllerManagerConfiguration,Controllers API rule violation: list_type_missing,k8s.io/controller-manager/config/v1alpha1,LeaderMigrationConfiguration,ControllerLeaders @@ -147,6 +146,8 @@ API rule violation: names_match,k8s.io/cloud-provider/config/v1alpha1,KubeCloudS API rule violation: names_match,k8s.io/cloud-provider/config/v1alpha1,KubeCloudSharedConfiguration,RouteReconciliationPeriod API rule violation: names_match,k8s.io/cloud-provider/config/v1alpha1,KubeCloudSharedConfiguration,UseServiceAccountCredentials API rule violation: names_match,k8s.io/cloud-provider/config/v1alpha1,WebhookConfiguration,Webhooks +API rule violation: names_match,k8s.io/cloud-provider/controllers/node/config/v1alpha1,NodeControllerConfiguration,ConcurrentNodeSyncs +API rule violation: names_match,k8s.io/cloud-provider/controllers/service/config/v1alpha1,ServiceControllerConfiguration,ConcurrentServiceSyncs API rule violation: names_match,k8s.io/controller-manager/config/v1alpha1,GenericControllerManagerConfiguration,Address API rule violation: names_match,k8s.io/controller-manager/config/v1alpha1,GenericControllerManagerConfiguration,ClientConnection API rule violation: names_match,k8s.io/controller-manager/config/v1alpha1,GenericControllerManagerConfiguration,ControllerStartInterval @@ -171,6 +172,7 @@ API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,C API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CronJobControllerConfiguration,ConcurrentCronJobSyncs API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DaemonSetControllerConfiguration,ConcurrentDaemonSetSyncs API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DeploymentControllerConfiguration,ConcurrentDeploymentSyncs +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DeviceTaintEvictionControllerConfiguration,ConcurrentSyncs API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointControllerConfiguration,ConcurrentEndpointSyncs API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointControllerConfiguration,EndpointUpdatesBatchPeriod API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointSliceControllerConfiguration,ConcurrentServiceEndpointSyncs @@ -198,6 +200,7 @@ API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,K API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DaemonSetController API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeploymentController API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeprecatedController +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeviceTaintEvictionController API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointController API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointSliceController API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointSliceMirroringController diff --git a/api/discovery/aggregated_v2.json b/api/discovery/aggregated_v2.json index 01dc28707d216..7a53c2e2a74e8 100644 --- a/api/discovery/aggregated_v2.json +++ b/api/discovery/aggregated_v2.json @@ -683,32 +683,6 @@ ], "version": "v1" }, - { - "freshness": "Current", - "resources": [ - { - "resource": "clustertrustbundles", - "responseKind": { - "group": "", - "kind": "ClusterTrustBundle", - "version": "" - }, - "scope": "Cluster", - "singularResource": "clustertrustbundle", - "verbs": [ - "create", - "delete", - "deletecollection", - "get", - "list", - "patch", - "update", - "watch" - ] - } - ], - "version": "v1beta1" - }, { "freshness": "Current", "resources": [ @@ -768,6 +742,32 @@ ] } ], + "version": "v1beta1" + }, + { + "freshness": "Current", + "resources": [ + { + "resource": "clustertrustbundles", + "responseKind": { + "group": "", + "kind": "ClusterTrustBundle", + "version": "" + }, + "scope": "Cluster", + "singularResource": "clustertrustbundle", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + } + ], "version": "v1alpha1" } ] @@ -1312,35 +1312,6 @@ } ], "version": "v1beta1" - }, - { - "freshness": "Current", - "resources": [ - { - "resource": "volumeattributesclasses", - "responseKind": { - "group": "", - "kind": "VolumeAttributesClass", - "version": "" - }, - "scope": "Cluster", - "shortNames": [ - "vac" - ], - "singularResource": "volumeattributesclass", - "verbs": [ - "create", - "delete", - "deletecollection", - "get", - "list", - "patch", - "update", - "watch" - ] - } - ], - "version": "v1alpha1" } ] }, @@ -1656,6 +1627,32 @@ } ], "version": "v1" + }, + { + "freshness": "Current", + "resources": [ + { + "resource": "workloads", + "responseKind": { + "group": "", + "kind": "Workload", + "version": "" + }, + "scope": "Namespaced", + "singularResource": "workload", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + } + ], + "version": "v1alpha1" } ] }, @@ -2130,6 +2127,21 @@ }, "scope": "Cluster", "singularResource": "devicetaintrule", + "subresources": [ + { + "responseKind": { + "group": "", + "kind": "DeviceTaintRule", + "version": "" + }, + "subresource": "status", + "verbs": [ + "get", + "patch", + "update" + ] + } + ], "verbs": [ "create", "delete", @@ -2321,7 +2333,7 @@ ] } ], - "version": "v1alpha1" + "version": "v1beta1" } ] } diff --git a/api/discovery/apis.json b/api/discovery/apis.json index f988f48285873..ae395b2e4a089 100644 --- a/api/discovery/apis.json +++ b/api/discovery/apis.json @@ -174,10 +174,6 @@ { "groupVersion": "storage.k8s.io/v1beta1", "version": "v1beta1" - }, - { - "groupVersion": "storage.k8s.io/v1alpha1", - "version": "v1alpha1" } ] }, @@ -225,6 +221,10 @@ { "groupVersion": "scheduling.k8s.io/v1", "version": "v1" + }, + { + "groupVersion": "scheduling.k8s.io/v1alpha1", + "version": "v1alpha1" } ] }, @@ -329,13 +329,13 @@ { "name": "storagemigration.k8s.io", "preferredVersion": { - "groupVersion": "storagemigration.k8s.io/v1alpha1", - "version": "v1alpha1" + "groupVersion": "storagemigration.k8s.io/v1beta1", + "version": "v1beta1" }, "versions": [ { - "groupVersion": "storagemigration.k8s.io/v1alpha1", - "version": "v1alpha1" + "groupVersion": "storagemigration.k8s.io/v1beta1", + "version": "v1beta1" } ] } diff --git a/api/discovery/apis__admissionregistration.k8s.io__v1alpha1.json b/api/discovery/apis__admissionregistration.k8s.io__v1alpha1.json index 13f98d6a8aa0d..d8e54bcfe2597 100644 --- a/api/discovery/apis__admissionregistration.k8s.io__v1alpha1.json +++ b/api/discovery/apis__admissionregistration.k8s.io__v1alpha1.json @@ -11,7 +11,7 @@ "name": "mutatingadmissionpolicies", "namespaced": false, "singularName": "mutatingadmissionpolicy", - "storageVersionHash": "lP2+lF8aHIY=", + "storageVersionHash": "LYmCf+UMVdg=", "verbs": [ "create", "delete", @@ -31,7 +31,7 @@ "name": "mutatingadmissionpolicybindings", "namespaced": false, "singularName": "mutatingadmissionpolicybinding", - "storageVersionHash": "Q2Qe566oRi8=", + "storageVersionHash": "90V5FRZZ3Zg=", "verbs": [ "create", "delete", diff --git a/api/discovery/apis__admissionregistration.k8s.io__v1beta1.json b/api/discovery/apis__admissionregistration.k8s.io__v1beta1.json index 26fbd2f7303d2..47eb7c592ca73 100644 --- a/api/discovery/apis__admissionregistration.k8s.io__v1beta1.json +++ b/api/discovery/apis__admissionregistration.k8s.io__v1beta1.json @@ -11,7 +11,7 @@ "name": "mutatingadmissionpolicies", "namespaced": false, "singularName": "mutatingadmissionpolicy", - "storageVersionHash": "lP2+lF8aHIY=", + "storageVersionHash": "LYmCf+UMVdg=", "verbs": [ "create", "delete", @@ -31,7 +31,7 @@ "name": "mutatingadmissionpolicybindings", "namespaced": false, "singularName": "mutatingadmissionpolicybinding", - "storageVersionHash": "Q2Qe566oRi8=", + "storageVersionHash": "90V5FRZZ3Zg=", "verbs": [ "create", "delete", diff --git a/api/discovery/apis__certificates.k8s.io__v1alpha1.json b/api/discovery/apis__certificates.k8s.io__v1alpha1.json index daee682511fbc..c936702ea4bca 100644 --- a/api/discovery/apis__certificates.k8s.io__v1alpha1.json +++ b/api/discovery/apis__certificates.k8s.io__v1alpha1.json @@ -19,34 +19,6 @@ "update", "watch" ] - }, - { - "kind": "PodCertificateRequest", - "name": "podcertificaterequests", - "namespaced": true, - "singularName": "podcertificaterequest", - "storageVersionHash": "3F2mRg06NdI=", - "verbs": [ - "create", - "delete", - "deletecollection", - "get", - "list", - "patch", - "update", - "watch" - ] - }, - { - "kind": "PodCertificateRequest", - "name": "podcertificaterequests/status", - "namespaced": true, - "singularName": "", - "verbs": [ - "get", - "patch", - "update" - ] } ] } diff --git a/api/discovery/apis__certificates.k8s.io__v1beta1.json b/api/discovery/apis__certificates.k8s.io__v1beta1.json index f1bf138d51619..3ea6c3087662a 100644 --- a/api/discovery/apis__certificates.k8s.io__v1beta1.json +++ b/api/discovery/apis__certificates.k8s.io__v1beta1.json @@ -19,6 +19,34 @@ "update", "watch" ] + }, + { + "kind": "PodCertificateRequest", + "name": "podcertificaterequests", + "namespaced": true, + "singularName": "podcertificaterequest", + "storageVersionHash": "wYA9yXQH8fg=", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + }, + { + "kind": "PodCertificateRequest", + "name": "podcertificaterequests/status", + "namespaced": true, + "singularName": "", + "verbs": [ + "get", + "patch", + "update" + ] } ] } diff --git a/api/discovery/apis__resource.k8s.io__v1.json b/api/discovery/apis__resource.k8s.io__v1.json index 41f9470526510..8a3337939b6e5 100644 --- a/api/discovery/apis__resource.k8s.io__v1.json +++ b/api/discovery/apis__resource.k8s.io__v1.json @@ -8,7 +8,7 @@ "name": "deviceclasses", "namespaced": false, "singularName": "deviceclass", - "storageVersionHash": "weQRMT6DeYM=", + "storageVersionHash": "Yk2PTc1Ybxk=", "verbs": [ "create", "delete", @@ -25,7 +25,7 @@ "name": "resourceclaims", "namespaced": true, "singularName": "resourceclaim", - "storageVersionHash": "EJAWH5WrAYg=", + "storageVersionHash": "wgAZaHcZxUg=", "verbs": [ "create", "delete", @@ -53,7 +53,7 @@ "name": "resourceclaimtemplates", "namespaced": true, "singularName": "resourceclaimtemplate", - "storageVersionHash": "24m0okHrUtk=", + "storageVersionHash": "TuzjC49aUfM=", "verbs": [ "create", "delete", @@ -70,7 +70,7 @@ "name": "resourceslices", "namespaced": false, "singularName": "resourceslice", - "storageVersionHash": "z6Bc9vgk6yE=", + "storageVersionHash": "KsC072WgaEY=", "verbs": [ "create", "delete", diff --git a/api/discovery/apis__resource.k8s.io__v1alpha3.json b/api/discovery/apis__resource.k8s.io__v1alpha3.json index 70b14d2a6775a..9e91b4e383be8 100644 --- a/api/discovery/apis__resource.k8s.io__v1alpha3.json +++ b/api/discovery/apis__resource.k8s.io__v1alpha3.json @@ -19,6 +19,17 @@ "update", "watch" ] + }, + { + "kind": "DeviceTaintRule", + "name": "devicetaintrules/status", + "namespaced": false, + "singularName": "", + "verbs": [ + "get", + "patch", + "update" + ] } ] } diff --git a/api/discovery/apis__resource.k8s.io__v1beta1.json b/api/discovery/apis__resource.k8s.io__v1beta1.json index 7be02cb32efcf..aef0a9d134fd4 100644 --- a/api/discovery/apis__resource.k8s.io__v1beta1.json +++ b/api/discovery/apis__resource.k8s.io__v1beta1.json @@ -8,7 +8,7 @@ "name": "deviceclasses", "namespaced": false, "singularName": "deviceclass", - "storageVersionHash": "weQRMT6DeYM=", + "storageVersionHash": "Yk2PTc1Ybxk=", "verbs": [ "create", "delete", @@ -25,7 +25,7 @@ "name": "resourceclaims", "namespaced": true, "singularName": "resourceclaim", - "storageVersionHash": "EJAWH5WrAYg=", + "storageVersionHash": "wgAZaHcZxUg=", "verbs": [ "create", "delete", @@ -53,7 +53,7 @@ "name": "resourceclaimtemplates", "namespaced": true, "singularName": "resourceclaimtemplate", - "storageVersionHash": "24m0okHrUtk=", + "storageVersionHash": "TuzjC49aUfM=", "verbs": [ "create", "delete", @@ -70,7 +70,7 @@ "name": "resourceslices", "namespaced": false, "singularName": "resourceslice", - "storageVersionHash": "z6Bc9vgk6yE=", + "storageVersionHash": "KsC072WgaEY=", "verbs": [ "create", "delete", diff --git a/api/discovery/apis__resource.k8s.io__v1beta2.json b/api/discovery/apis__resource.k8s.io__v1beta2.json index c263ff9dcaf13..02e161222486b 100644 --- a/api/discovery/apis__resource.k8s.io__v1beta2.json +++ b/api/discovery/apis__resource.k8s.io__v1beta2.json @@ -8,7 +8,7 @@ "name": "deviceclasses", "namespaced": false, "singularName": "deviceclass", - "storageVersionHash": "weQRMT6DeYM=", + "storageVersionHash": "Yk2PTc1Ybxk=", "verbs": [ "create", "delete", @@ -25,7 +25,7 @@ "name": "resourceclaims", "namespaced": true, "singularName": "resourceclaim", - "storageVersionHash": "EJAWH5WrAYg=", + "storageVersionHash": "wgAZaHcZxUg=", "verbs": [ "create", "delete", @@ -53,7 +53,7 @@ "name": "resourceclaimtemplates", "namespaced": true, "singularName": "resourceclaimtemplate", - "storageVersionHash": "24m0okHrUtk=", + "storageVersionHash": "TuzjC49aUfM=", "verbs": [ "create", "delete", @@ -70,7 +70,7 @@ "name": "resourceslices", "namespaced": false, "singularName": "resourceslice", - "storageVersionHash": "z6Bc9vgk6yE=", + "storageVersionHash": "KsC072WgaEY=", "verbs": [ "create", "delete", diff --git a/api/discovery/apis__scheduling.k8s.io.json b/api/discovery/apis__scheduling.k8s.io.json index 3782996ff7f06..a8f4e435bfb3b 100644 --- a/api/discovery/apis__scheduling.k8s.io.json +++ b/api/discovery/apis__scheduling.k8s.io.json @@ -10,6 +10,10 @@ { "groupVersion": "scheduling.k8s.io/v1", "version": "v1" + }, + { + "groupVersion": "scheduling.k8s.io/v1alpha1", + "version": "v1alpha1" } ] } diff --git a/api/discovery/apis__scheduling.k8s.io__v1alpha1.json b/api/discovery/apis__scheduling.k8s.io__v1alpha1.json new file mode 100644 index 0000000000000..8bc6b402acd3a --- /dev/null +++ b/api/discovery/apis__scheduling.k8s.io__v1alpha1.json @@ -0,0 +1,24 @@ +{ + "apiVersion": "v1", + "groupVersion": "scheduling.k8s.io/v1alpha1", + "kind": "APIResourceList", + "resources": [ + { + "kind": "Workload", + "name": "workloads", + "namespaced": true, + "singularName": "workload", + "storageVersionHash": "7X3nQnlLxCA=", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + } + ] +} diff --git a/api/discovery/apis__storage.k8s.io.json b/api/discovery/apis__storage.k8s.io.json index afb58ad6c0d18..e7e7ab7dc659f 100644 --- a/api/discovery/apis__storage.k8s.io.json +++ b/api/discovery/apis__storage.k8s.io.json @@ -14,10 +14,6 @@ { "groupVersion": "storage.k8s.io/v1beta1", "version": "v1beta1" - }, - { - "groupVersion": "storage.k8s.io/v1alpha1", - "version": "v1alpha1" } ] } diff --git a/api/discovery/apis__storage.k8s.io__v1alpha1.json b/api/discovery/apis__storage.k8s.io__v1alpha1.json deleted file mode 100644 index 91b4d008563a8..0000000000000 --- a/api/discovery/apis__storage.k8s.io__v1alpha1.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "apiVersion": "v1", - "groupVersion": "storage.k8s.io/v1alpha1", - "kind": "APIResourceList", - "resources": [ - { - "kind": "VolumeAttributesClass", - "name": "volumeattributesclasses", - "namespaced": false, - "shortNames": [ - "vac" - ], - "singularName": "volumeattributesclass", - "storageVersionHash": "Bl3MtjZ/n/s=", - "verbs": [ - "create", - "delete", - "deletecollection", - "get", - "list", - "patch", - "update", - "watch" - ] - } - ] -} diff --git a/api/discovery/apis__storagemigration.k8s.io.json b/api/discovery/apis__storagemigration.k8s.io.json index 2299ca4699d98..863b74b928d42 100644 --- a/api/discovery/apis__storagemigration.k8s.io.json +++ b/api/discovery/apis__storagemigration.k8s.io.json @@ -3,13 +3,13 @@ "kind": "APIGroup", "name": "storagemigration.k8s.io", "preferredVersion": { - "groupVersion": "storagemigration.k8s.io/v1alpha1", - "version": "v1alpha1" + "groupVersion": "storagemigration.k8s.io/v1beta1", + "version": "v1beta1" }, "versions": [ { - "groupVersion": "storagemigration.k8s.io/v1alpha1", - "version": "v1alpha1" + "groupVersion": "storagemigration.k8s.io/v1beta1", + "version": "v1beta1" } ] } diff --git a/api/discovery/apis__storagemigration.k8s.io__v1alpha1.json b/api/discovery/apis__storagemigration.k8s.io__v1alpha1.json deleted file mode 100644 index 1033ba3ae4fec..0000000000000 --- a/api/discovery/apis__storagemigration.k8s.io__v1alpha1.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "apiVersion": "v1", - "groupVersion": "storagemigration.k8s.io/v1alpha1", - "kind": "APIResourceList", - "resources": [ - { - "kind": "StorageVersionMigration", - "name": "storageversionmigrations", - "namespaced": false, - "singularName": "storageversionmigration", - "storageVersionHash": "N0mJdFqO17c=", - "verbs": [ - "create", - "delete", - "deletecollection", - "get", - "list", - "patch", - "update", - "watch" - ] - }, - { - "kind": "StorageVersionMigration", - "name": "storageversionmigrations/status", - "namespaced": false, - "singularName": "", - "verbs": [ - "get", - "patch", - "update" - ] - } - ] -} diff --git a/api/discovery/apis__storagemigration.k8s.io__v1beta1.json b/api/discovery/apis__storagemigration.k8s.io__v1beta1.json new file mode 100644 index 0000000000000..0fb2e76daad58 --- /dev/null +++ b/api/discovery/apis__storagemigration.k8s.io__v1beta1.json @@ -0,0 +1,35 @@ +{ + "apiVersion": "v1", + "groupVersion": "storagemigration.k8s.io/v1beta1", + "kind": "APIResourceList", + "resources": [ + { + "kind": "StorageVersionMigration", + "name": "storageversionmigrations", + "namespaced": false, + "singularName": "storageversionmigration", + "storageVersionHash": "pCjmmKOkLy4=", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + }, + { + "kind": "StorageVersionMigration", + "name": "storageversionmigrations/status", + "namespaced": false, + "singularName": "", + "verbs": [ + "get", + "patch", + "update" + ] + } + ] +} diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 305a8a0ee5dcf..2e0f73febff22 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -2314,7 +2314,7 @@ "type": "integer" }, "terminatingReplicas": { - "description": "Total number of terminating pods targeted by this deployment. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field.", + "description": "Total number of terminating pods targeted by this deployment. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default).", "format": "int32", "type": "integer" }, @@ -2512,7 +2512,7 @@ "type": "integer" }, "terminatingReplicas": { - "description": "The number of terminating pods for this replica set. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field.", + "description": "The number of terminating pods for this replica set. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default).", "format": "int32", "type": "integer" } @@ -2555,7 +2555,7 @@ "properties": { "maxUnavailable": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString", - "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is alpha-level and is only honored by servers that enable the MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable." + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is beta-level and is enabled by default. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable. This setting might not be effective for the OrderedReady podManagementPolicy. That policy ensures pods are created and become ready one at a time." }, "partition": { "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned for updates. During a rolling update, all pods from ordinal Replicas-1 to Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. This is helpful in being able to do a canary based deployment. The default value is 0.", @@ -3853,7 +3853,7 @@ "type": "object" }, "io.k8s.api.autoscaling.v2.HPAScalingRules": { - "description": "HPAScalingRules configures the scaling behavior for one direction via scaling Policy Rules and a configurable metric tolerance.\n\nScaling Policy Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.\n\nThe tolerance is applied to the metric values and prevents scaling too eagerly for small metric variations. (Note that setting a tolerance requires enabling the alpha HPAConfigurableTolerance feature gate.)", + "description": "HPAScalingRules configures the scaling behavior for one direction via scaling Policy Rules and a configurable metric tolerance.\n\nScaling Policy Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.\n\nThe tolerance is applied to the metric values and prevents scaling too eagerly for small metric variations. (Note that setting a tolerance requires the beta HPAConfigurableTolerance feature gate to be enabled.)", "properties": { "policies": { "description": "policies is a list of potential scaling polices which can be used during scaling. If not set, use the default values: - For scale up: allow doubling the number of pods, or an absolute change of 4 pods in a 15s window. - For scale down: allow all pods to be removed in a 15s window.", @@ -3874,7 +3874,7 @@ }, "tolerance": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity", - "description": "tolerance is the tolerance on the ratio between the current and desired metric value under which no updates are made to the desired number of replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not set, the default cluster-wide tolerance is applied (by default 10%).\n\nFor example, if autoscaling is configured with a memory consumption target of 100Mi, and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be triggered when the actual consumption falls below 95Mi or exceeds 101Mi.\n\nThis is an alpha field and requires enabling the HPAConfigurableTolerance feature gate." + "description": "tolerance is the tolerance on the ratio between the current and desired metric value under which no updates are made to the desired number of replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not set, the default cluster-wide tolerance is applied (by default 10%).\n\nFor example, if autoscaling is configured with a memory consumption target of 100Mi, and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be triggered when the actual consumption falls below 95Mi or exceeds 101Mi.\n\nThis is an beta field and requires the HPAConfigurableTolerance feature gate to be enabled." } }, "type": "object" @@ -4587,7 +4587,7 @@ "type": "integer" }, "managedBy": { - "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `kubernetes.io/job-controller`, but skips reconciling Jobs with a custom value for this field. The value must be a valid domain-prefixed path (e.g. acme.io/foo) - all characters before the first \"/\" must be a valid subdomain as defined by RFC 1123. All characters trailing the first \"/\" must be valid HTTP Path characters as defined by RFC 3986. The value cannot exceed 63 characters. This field is immutable.\n\nThis field is beta-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (enabled by default).", + "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `kubernetes.io/job-controller`, but skips reconciling Jobs with a custom value for this field. The value must be a valid domain-prefixed path (e.g. acme.io/foo) - all characters before the first \"/\" must be a valid subdomain as defined by RFC 1123. All characters trailing the first \"/\" must be valid HTTP Path characters as defined by RFC 3986. The value cannot exceed 63 characters. This field is immutable.", "type": "string" }, "manualSelector": { @@ -4771,8 +4771,7 @@ } }, "required": [ - "type", - "status" + "type" ], "type": "object" }, @@ -4987,8 +4986,7 @@ "request": { "description": "request contains an x509 certificate signing request encoded in a \"CERTIFICATE REQUEST\" PEM block. When serialized as JSON or YAML, the data is additionally base64-encoded.", "format": "byte", - "type": "string", - "x-kubernetes-list-type": "atomic" + "type": "string" }, "signerName": { "description": "signerName indicates the requested signer, and is a qualified name.\n\nList/watch requests for CertificateSigningRequests can filter on this field using a \"spec.signerName=NAME\" fieldSelector.\n\nWell-known Kubernetes signers are:\n 1. \"kubernetes.io/kube-apiserver-client\": issues client certificates that can be used to authenticate to kube-apiserver.\n Requests for this signer are never auto-approved by kube-controller-manager, can be issued by the \"csrsigning\" controller in kube-controller-manager.\n 2. \"kubernetes.io/kube-apiserver-client-kubelet\": issues client certificates that kubelets use to authenticate to kube-apiserver.\n Requests for this signer can be auto-approved by the \"csrapproving\" controller in kube-controller-manager, and can be issued by the \"csrsigning\" controller in kube-controller-manager.\n 3. \"kubernetes.io/kubelet-serving\" issues serving certificates that kubelets use to serve TLS endpoints, which kube-apiserver can connect to securely.\n Requests for this signer are never auto-approved by kube-controller-manager, and can be issued by the \"csrsigning\" controller in kube-controller-manager.\n\nMore details are available at https://k8s.io/docs/reference/access-authn-authz/certificate-signing-requests/#kubernetes-signers\n\nCustom signerNames can also be specified. The signer defines:\n 1. Trust distribution: how trust (CA bundles) are distributed.\n 2. Permitted subjects: and behavior when a disallowed subject is requested.\n 3. Required, permitted, or forbidden x509 extensions in the request (including whether subjectAltNames are allowed, which types, restrictions on allowed values) and behavior when a disallowed extension is requested.\n 4. Required, permitted, or forbidden key usages / extended key usages.\n 5. Expiration/certificate lifetime: whether it is fixed by the signer, configurable by the admin.\n 6. Whether or not requests for CA certificates are allowed.", @@ -5023,8 +5021,7 @@ "certificate": { "description": "certificate is populated with an issued certificate by the signer after an Approved condition is present. This field is set via the /status subresource. Once populated, this field is immutable.\n\nIf the certificate signing request is denied, a condition of type \"Denied\" is added and this field remains empty. If the signer cannot issue the certificate, a condition of type \"Failed\" is added and this field remains empty.\n\nValidation requirements:\n 1. certificate must contain one or more PEM blocks.\n 2. All PEM blocks must have the \"CERTIFICATE\" label, contain no headers, and the encoded data\n must be a BER-encoded ASN.1 Certificate structure as described in section 4 of RFC5280.\n 3. Non-PEM content may appear before or after the \"CERTIFICATE\" PEM blocks and is unvalidated,\n to allow for explanatory text as described in section 5.2 of RFC7468.\n\nIf more than one PEM block is present, and the definition of the requested spec.signerName does not indicate otherwise, the first block is the issued certificate, and subsequent blocks should be treated as intermediate certificates and presented in TLS handshakes.\n\nThe certificate is encoded in PEM format.\n\nWhen serialized as JSON or YAML, the data is additionally base64-encoded, so it consists of:\n\n base64(\n -----BEGIN CERTIFICATE-----\n ...\n -----END CERTIFICATE-----\n )", "format": "byte", - "type": "string", - "x-kubernetes-list-type": "atomic" + "type": "string" }, "conditions": { "description": "conditions applied to the request. Known conditions are \"Approved\", \"Denied\", and \"Failed\".", @@ -5124,7 +5121,91 @@ ], "type": "object" }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequest": { + "io.k8s.api.certificates.v1beta1.ClusterTrustBundle": { + "description": "ClusterTrustBundle is a cluster-scoped container for X.509 trust anchors (root certificates).\n\nClusterTrustBundle objects are considered to be readable by any authenticated user in the cluster, because they can be mounted by pods using the `clusterTrustBundle` projection. All service accounts have read access to ClusterTrustBundles by default. Users who only have namespace-level access to a cluster can read ClusterTrustBundles by impersonating a serviceaccount that they have access to.\n\nIt can be optionally associated with a particular assigner, in which case it contains one valid set of trust anchors for that signer. Signers may have multiple associated ClusterTrustBundles; each is an independent set of trust anchors for that signer. Admission control is used to enforce that only users with permissions on the signer can create or modify the corresponding bundle.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "metadata contains the object metadata." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundleSpec", + "description": "spec contains the signer (if any) and trust anchors." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.ClusterTrustBundleList": { + "description": "ClusterTrustBundleList is a collection of ClusterTrustBundle objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a collection of ClusterTrustBundle objects", + "items": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "metadata contains the list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundleList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.ClusterTrustBundleSpec": { + "description": "ClusterTrustBundleSpec contains the signer and trust anchors.", + "properties": { + "signerName": { + "description": "signerName indicates the associated signer, if any.\n\nIn order to create or update a ClusterTrustBundle that sets signerName, you must have the following cluster-scoped permission: group=certificates.k8s.io resource=signers resourceName= verb=attest.\n\nIf signerName is not empty, then the ClusterTrustBundle object must be named with the signer name as a prefix (translating slashes to colons). For example, for the signer name `example.com/foo`, valid ClusterTrustBundle object names include `example.com:foo:abc` and `example.com:foo:v1`.\n\nIf signerName is empty, then the ClusterTrustBundle object's name must not have such a prefix.\n\nList/watch requests for ClusterTrustBundles can filter on this field using a `spec.signerName=NAME` field selector.", + "type": "string" + }, + "trustBundle": { + "description": "trustBundle contains the individual X.509 trust anchors for this bundle, as PEM bundle of PEM-wrapped, DER-formatted X.509 certificates.\n\nThe data must consist only of PEM certificate blocks that parse as valid X.509 certificates. Each certificate must include a basic constraints extension with the CA bit set. The API server will reject objects that contain duplicate certificates, or that use PEM block headers.\n\nUsers of ClusterTrustBundles, including Kubelet, are free to reorder and deduplicate certificate blocks in this file according to their own logic, as well as to drop PEM block headers and inter-block data.", + "type": "string" + } + }, + "required": [ + "trustBundle" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1beta1.PodCertificateRequest": { "description": "PodCertificateRequest encodes a pod requesting a certificate from a given signer.\n\nKubelets use this API to implement podCertificate projected volumes", "properties": { "apiVersion": { @@ -5140,11 +5221,11 @@ "description": "metadata contains the object metadata." }, "spec": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequestSpec", + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequestSpec", "description": "spec contains the details about the certificate being requested." }, "status": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequestStatus", + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequestStatus", "description": "status contains the issued certificate, and a standard set of conditions." } }, @@ -5156,11 +5237,11 @@ { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } ] }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestList": { + "io.k8s.api.certificates.v1beta1.PodCertificateRequestList": { "description": "PodCertificateRequestList is a collection of PodCertificateRequest objects", "properties": { "apiVersion": { @@ -5170,7 +5251,7 @@ "items": { "description": "items is a collection of PodCertificateRequest objects", "items": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" }, "type": "array" }, @@ -5191,11 +5272,11 @@ { "group": "certificates.k8s.io", "kind": "PodCertificateRequestList", - "version": "v1alpha1" + "version": "v1beta1" } ] }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestSpec": { + "io.k8s.api.certificates.v1beta1.PodCertificateRequestSpec": { "description": "PodCertificateRequestSpec describes the certificate request. All fields are immutable after creation.", "properties": { "maxExpirationSeconds": { @@ -5240,6 +5321,13 @@ "signerName": { "description": "signerName indicates the requested signer.\n\nAll signer names beginning with `kubernetes.io` are reserved for use by the Kubernetes project. There is currently one well-known signer documented by the Kubernetes project, `kubernetes.io/kube-apiserver-client-pod`, which will issue client certificates understood by kube-apiserver. It is currently unimplemented.", "type": "string" + }, + "unverifiedUserAnnotations": { + "additionalProperties": { + "type": "string" + }, + "description": "unverifiedUserAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" } }, "required": [ @@ -5255,7 +5343,7 @@ ], "type": "object" }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestStatus": { + "io.k8s.api.certificates.v1beta1.PodCertificateRequestStatus": { "description": "PodCertificateRequestStatus describes the status of the request, and holds the certificate data if the request is issued.", "properties": { "beginRefreshAt": { @@ -5290,90 +5378,6 @@ }, "type": "object" }, - "io.k8s.api.certificates.v1beta1.ClusterTrustBundle": { - "description": "ClusterTrustBundle is a cluster-scoped container for X.509 trust anchors (root certificates).\n\nClusterTrustBundle objects are considered to be readable by any authenticated user in the cluster, because they can be mounted by pods using the `clusterTrustBundle` projection. All service accounts have read access to ClusterTrustBundles by default. Users who only have namespace-level access to a cluster can read ClusterTrustBundles by impersonating a serviceaccount that they have access to.\n\nIt can be optionally associated with a particular assigner, in which case it contains one valid set of trust anchors for that signer. Signers may have multiple associated ClusterTrustBundles; each is an independent set of trust anchors for that signer. Admission control is used to enforce that only users with permissions on the signer can create or modify the corresponding bundle.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", - "description": "metadata contains the object metadata." - }, - "spec": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundleSpec", - "description": "spec contains the signer (if any) and trust anchors." - } - }, - "required": [ - "spec" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1beta1" - } - ] - }, - "io.k8s.api.certificates.v1beta1.ClusterTrustBundleList": { - "description": "ClusterTrustBundleList is a collection of ClusterTrustBundle objects", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "items is a collection of ClusterTrustBundle objects", - "items": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - }, - "type": "array" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", - "description": "metadata contains the list metadata." - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundleList", - "version": "v1beta1" - } - ] - }, - "io.k8s.api.certificates.v1beta1.ClusterTrustBundleSpec": { - "description": "ClusterTrustBundleSpec contains the signer and trust anchors.", - "properties": { - "signerName": { - "description": "signerName indicates the associated signer, if any.\n\nIn order to create or update a ClusterTrustBundle that sets signerName, you must have the following cluster-scoped permission: group=certificates.k8s.io resource=signers resourceName= verb=attest.\n\nIf signerName is not empty, then the ClusterTrustBundle object must be named with the signer name as a prefix (translating slashes to colons). For example, for the signer name `example.com/foo`, valid ClusterTrustBundle object names include `example.com:foo:abc` and `example.com:foo:v1`.\n\nIf signerName is empty, then the ClusterTrustBundle object's name must not have such a prefix.\n\nList/watch requests for ClusterTrustBundles can filter on this field using a `spec.signerName=NAME` field selector.", - "type": "string" - }, - "trustBundle": { - "description": "trustBundle contains the individual X.509 trust anchors for this bundle, as PEM bundle of PEM-wrapped, DER-formatted X.509 certificates.\n\nThe data must consist only of PEM certificate blocks that parse as valid X.509 certificates. Each certificate must include a basic constraints extension with the CA bit set. The API server will reject objects that contain duplicate certificates, or that use PEM block headers.\n\nUsers of ClusterTrustBundles, including Kubelet, are free to reorder and deduplicate certificate blocks in this file according to their own logic, as well as to drop PEM block headers and inter-block data.", - "type": "string" - } - }, - "required": [ - "trustBundle" - ], - "type": "object" - }, "io.k8s.api.coordination.v1.Lease": { "description": "Lease defines a lease concept.", "properties": { @@ -6517,7 +6521,7 @@ "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" }, "resizePolicy": { - "description": "Resources resize policy for the container.", + "description": "Resources resize policy for the container. This field cannot be set on ephemeral containers.", "items": { "$ref": "#/definitions/io.k8s.api.core.v1.ContainerResizePolicy" }, @@ -8899,6 +8903,14 @@ "$ref": "#/definitions/io.k8s.api.core.v1.NodeDaemonEndpoints", "description": "Endpoints of daemons running on the Node." }, + "declaredFeatures": { + "description": "DeclaredFeatures represents the features related to feature gates that are declared by the node.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, "features": { "$ref": "#/definitions/io.k8s.api.core.v1.NodeFeatures", "description": "Features describes the set of features implemented by the CRI implementation." @@ -9228,7 +9240,7 @@ }, "resources": { "$ref": "#/definitions/io.k8s.api.core.v1.VolumeResourceRequirements", - "description": "resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + "description": "resources represents the minimum resources the volume should have. Users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" }, "selector": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", @@ -9268,7 +9280,7 @@ "additionalProperties": { "type": "string" }, - "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object", "x-kubernetes-map-type": "granular" }, @@ -9276,7 +9288,7 @@ "additionalProperties": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" }, - "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object" }, "capacity": { @@ -9476,7 +9488,7 @@ }, "nodeAffinity": { "$ref": "#/definitions/io.k8s.api.core.v1.VolumeNodeAffinity", - "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume." + "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume. This field is mutable if MutablePVNodeAffinity feature gate is enabled." }, "persistentVolumeReclaimPolicy": { "description": "persistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", @@ -9713,6 +9725,13 @@ "signerName": { "description": "Kubelet's generated CSRs will be addressed to this signer.", "type": "string" + }, + "userAnnotations": { + "additionalProperties": { + "type": "string" + }, + "description": "userAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nThese values are copied verbatim into the `spec.unverifiedUserAnnotations` field of the PodCertificateRequest objects that Kubelet creates.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" } }, "required": [ @@ -9737,7 +9756,7 @@ "type": "string" }, "observedGeneration": { - "description": "If set, this represents the .metadata.generation that the pod condition was set based upon. This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field.", + "description": "If set, this represents the .metadata.generation that the pod condition was set based upon. The PodObservedGenerationTracking feature gate must be enabled to use this field.", "format": "int64", "type": "integer" }, @@ -10183,7 +10202,7 @@ "x-kubernetes-list-type": "atomic" }, "resourceClaims": { - "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is a stable field but requires that the DynamicResourceAllocation feature gate is enabled.\n\nThis field is immutable.", "items": { "$ref": "#/definitions/io.k8s.api.core.v1.PodResourceClaim" }, @@ -10287,6 +10306,10 @@ "x-kubernetes-list-type": "map", "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "workloadRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.WorkloadReference", + "description": "WorkloadRef provides a reference to the Workload object that this Pod belongs to. This field is used by the scheduler to identify the PodGroup and apply the correct group scheduling policies. The Workload object referenced by this field may not exist at the time the Pod is created. This field is immutable, but a Workload object with the same name may be recreated with different policies. Doing this during pod scheduling may result in the placement not conforming to the expected policies." } }, "required": [ @@ -10297,6 +10320,13 @@ "io.k8s.api.core.v1.PodStatus": { "description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", "properties": { + "allocatedResources": { + "additionalProperties": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "AllocatedResources is the total requests allocated for this pod by the node. If pod-level requests are not set, this will be the total requests aggregated across containers in the pod.", + "type": "object" + }, "conditions": { "description": "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", "items": { @@ -10361,7 +10391,7 @@ "type": "string" }, "observedGeneration": { - "description": "If set, this represents the .metadata.generation that the pod status was set based upon. This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field.", + "description": "If set, this represents the .metadata.generation that the pod status was set based upon. The PodObservedGenerationTracking feature gate must be enabled to use this field.", "format": "int64", "type": "integer" }, @@ -10411,6 +10441,10 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", + "description": "Resources represents the compute resource requests and limits that have been applied at the pod level if pod-level requests or limits are set in PodSpec.Resources" + }, "startTime": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", "description": "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod." @@ -12081,7 +12115,7 @@ "type": "string" }, "operator": { - "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "description": "Operator represents a key's relationship to the value. Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).", "type": "string" }, "tolerationSeconds": { @@ -12572,6 +12606,28 @@ }, "type": "object" }, + "io.k8s.api.core.v1.WorkloadReference": { + "description": "WorkloadReference identifies the Workload object and PodGroup membership that a Pod belongs to. The scheduler uses this information to apply workload-aware scheduling semantics.", + "properties": { + "name": { + "description": "Name defines the name of the Workload object this Pod belongs to. Workload must be in the same namespace as the Pod. If it doesn't match any existing Workload, the Pod will remain unschedulable until a Workload object is created and observed by the kube-scheduler. It must be a DNS subdomain.", + "type": "string" + }, + "podGroup": { + "description": "PodGroup is the name of the PodGroup within the Workload that this Pod belongs to. If it doesn't match any existing PodGroup within the Workload, the Pod will remain unschedulable until the Workload object is recreated and observed by the kube-scheduler. It must be a DNS label.", + "type": "string" + }, + "podGroupReplicaKey": { + "description": "PodGroupReplicaKey specifies the replica key of the PodGroup to which this Pod belongs. It is used to distinguish pods belonging to different replicas of the same pod group. The pod group policy is applied separately to each replica. When set, it must be a DNS label.", + "type": "string" + } + }, + "required": [ + "name", + "podGroup" + ], + "type": "object" + }, "io.k8s.api.discovery.v1.Endpoint": { "description": "Endpoint represents a single logical \"backend\" implementing a service.", "properties": { @@ -12642,7 +12698,7 @@ "description": "EndpointHints provides hints describing how an endpoint should be consumed.", "properties": { "forNodes": { - "description": "forNodes indicates the node(s) this endpoint should be consumed by when using topology aware routing. May contain a maximum of 8 entries. This is an Alpha feature and is only used when the PreferSameTrafficDistribution feature gate is enabled.", + "description": "forNodes indicates the node(s) this endpoint should be consumed by when using topology aware routing. May contain a maximum of 8 entries.", "items": { "$ref": "#/definitions/io.k8s.api.discovery.v1.ForNode" }, @@ -15231,7 +15287,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -15355,13 +15411,13 @@ "type": "object" }, "io.k8s.api.resource.v1.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { "$ref": "#/definitions/io.k8s.api.resource.v1.Counter" }, - "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters in all sets is 32.", + "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters is 32.", "type": "object" }, "name": { @@ -15421,7 +15477,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1.DeviceCounterConsumption" }, @@ -15441,7 +15497,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1.DeviceTaint" }, @@ -15725,7 +15781,7 @@ "additionalProperties": { "$ref": "#/definitions/io.k8s.api.resource.v1.Counter" }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -15795,7 +15851,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -15888,7 +15944,7 @@ "description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.", "properties": { "effect": { - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -16009,7 +16065,7 @@ "description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.", "properties": { "driver": { - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -16352,7 +16408,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1.Device" }, @@ -16360,7 +16416,7 @@ "x-kubernetes-list-type": "atomic" }, "driver": { - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -16380,7 +16436,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1.CounterSet" }, @@ -16394,34 +16450,11 @@ ], "type": "object" }, - "io.k8s.api.resource.v1alpha3.CELDeviceSelector": { - "description": "CELDeviceSelector contains a CEL expression for selecting a device.", - "properties": { - "expression": { - "description": "Expression is a CEL expression which evaluates a single device. It must evaluate to true when the device under consideration satisfies the desired criteria, and false when it does not. Any other result is an error and causes allocation of devices to abort.\n\nThe expression's input is an object named \"device\", which carries the following properties:\n - driver (string): the name of the driver which defines this device.\n - attributes (map[string]object): the device's attributes, grouped by prefix\n (e.g. device.attributes[\"dra.example.com\"] evaluates to an object with all\n of the attributes which were prefixed by \"dra.example.com\".\n - capacity (map[string]object): the device's capacities, grouped by prefix.\n\nExample: Consider a device with driver=\"dra.example.com\", which exposes two attributes named \"model\" and \"ext.example.com/family\" and which exposes one capacity named \"modules\". This input to this expression would have the following fields:\n\n device.driver\n device.attributes[\"dra.example.com\"].model\n device.attributes[\"ext.example.com\"].family\n device.capacity[\"dra.example.com\"].modules\n\nThe device.driver field can be used to check for a specific driver, either as a high-level precondition (i.e. you only want to consider devices from this driver) or as part of a multi-clause expression that is meant to consider devices from different drivers.\n\nThe value type of each attribute is defined by the device definition, and users who write these expressions must consult the documentation for their specific drivers. The value type of each capacity is Quantity.\n\nIf an unknown prefix is used as a lookup in either device.attributes or device.capacity, an empty map will be returned. Any reference to an unknown field will cause an evaluation error and allocation to abort.\n\nA robust expression should check for the existence of attributes before referencing them.\n\nFor ease of use, the cel.bind() function is enabled, and can be used to simplify expressions that access multiple attributes with the same domain. For example:\n\n cel.bind(dra, device.attributes[\"dra.example.com\"], dra.someBool && dra.anotherBool)\n\nThe length of the expression must be smaller or equal to 10 Ki. The cost of evaluating it is also limited based on the estimated number of logical steps.", - "type": "string" - } - }, - "required": [ - "expression" - ], - "type": "object" - }, - "io.k8s.api.resource.v1alpha3.DeviceSelector": { - "description": "DeviceSelector must have exactly one field set.", - "properties": { - "cel": { - "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.CELDeviceSelector", - "description": "CEL contains a CEL expression for selecting a device." - } - }, - "type": "object" - }, "io.k8s.api.resource.v1alpha3.DeviceTaint": { "description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.", "properties": { "effect": { - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -16461,6 +16494,10 @@ "spec": { "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRuleSpec", "description": "Spec specifies the selector and one taint.\n\nChanging the spec automatically increments the metadata.generation number." + }, + "status": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus", + "description": "Status provides information about what was requested in the spec." } }, "required": [ @@ -16515,7 +16552,7 @@ "properties": { "deviceSelector": { "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintSelector", - "description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches." + "description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches." }, "taint": { "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaint", @@ -16527,6 +16564,25 @@ ], "type": "object" }, + "io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus": { + "description": "DeviceTaintRuleStatus provides information about an on-going pod eviction.", + "properties": { + "conditions": { + "description": "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.", + "items": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, "io.k8s.api.resource.v1alpha3.DeviceTaintSelector": { "description": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.", "properties": { @@ -16534,10 +16590,6 @@ "description": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.", "type": "string" }, - "deviceClassName": { - "description": "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.", - "type": "string" - }, "driver": { "description": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.", "type": "string" @@ -16545,14 +16597,6 @@ "pool": { "description": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.", "type": "string" - }, - "selectors": { - "description": "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.", - "items": { - "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceSelector" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" } }, "type": "object" @@ -16580,7 +16624,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -16667,7 +16711,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta1.DeviceCounterConsumption" }, @@ -16683,7 +16727,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta1.DeviceTaint" }, @@ -16776,7 +16820,7 @@ "type": "object" }, "io.k8s.api.resource.v1beta1.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { @@ -17084,7 +17128,7 @@ "additionalProperties": { "$ref": "#/definitions/io.k8s.api.resource.v1beta1.Counter" }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -17187,7 +17231,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -17280,7 +17324,7 @@ "description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.", "properties": { "effect": { - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -17355,7 +17399,7 @@ "description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.", "properties": { "driver": { - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -17698,7 +17742,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta1.Device" }, @@ -17706,7 +17750,7 @@ "x-kubernetes-list-type": "atomic" }, "driver": { - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -17726,7 +17770,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of SharedCounters is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta1.CounterSet" }, @@ -17763,7 +17807,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -17887,13 +17931,13 @@ "type": "object" }, "io.k8s.api.resource.v1beta2.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.Counter" }, - "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters in all sets is 32.", + "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters is 32.", "type": "object" }, "name": { @@ -17953,7 +17997,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.DeviceCounterConsumption" }, @@ -17973,7 +18017,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.DeviceTaint" }, @@ -18257,7 +18301,7 @@ "additionalProperties": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.Counter" }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -18327,7 +18371,7 @@ "type": "string" }, "driver": { - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -18420,7 +18464,7 @@ "description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.", "properties": { "effect": { - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -18541,7 +18585,7 @@ "description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.", "properties": { "driver": { - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -18884,7 +18928,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.Device" }, @@ -18892,7 +18936,7 @@ "x-kubernetes-list-type": "atomic" }, "driver": { - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -18912,7 +18956,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "$ref": "#/definitions/io.k8s.api.resource.v1beta2.CounterSet" }, @@ -19006,6 +19050,169 @@ } ] }, + "io.k8s.api.scheduling.v1alpha1.BasicSchedulingPolicy": { + "description": "BasicSchedulingPolicy indicates that standard Kubernetes scheduling behavior should be used.", + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.GangSchedulingPolicy": { + "description": "GangSchedulingPolicy defines the parameters for gang scheduling.", + "properties": { + "minCount": { + "description": "MinCount is the minimum number of pods that must be schedulable or scheduled at the same time for the scheduler to admit the entire group. It must be a positive integer.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "minCount" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.PodGroup": { + "description": "PodGroup represents a set of pods with a common scheduling policy.", + "properties": { + "name": { + "description": "Name is a unique identifier for the PodGroup within the Workload. It must be a DNS label. This field is immutable.", + "type": "string" + }, + "policy": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.PodGroupPolicy", + "description": "Policy defines the scheduling policy for this PodGroup." + } + }, + "required": [ + "name", + "policy" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.PodGroupPolicy": { + "description": "PodGroupPolicy defines the scheduling configuration for a PodGroup.", + "properties": { + "basic": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.BasicSchedulingPolicy", + "description": "Basic specifies that the pods in this group should be scheduled using standard Kubernetes scheduling behavior." + }, + "gang": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.GangSchedulingPolicy", + "description": "Gang specifies that the pods in this group should be scheduled using all-or-nothing semantics." + } + }, + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.TypedLocalObjectReference": { + "description": "TypedLocalObjectReference allows to reference typed object inside the same namespace.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. If APIGroup is empty, the specified Kind must be in the core API group. For any other third-party types, setting APIGroup is required. It must be a DNS subdomain.", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource being referenced. It must be a path segment name.", + "type": "string" + }, + "name": { + "description": "Name is the name of resource being referenced. It must be a path segment name.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.Workload": { + "description": "Workload allows for expressing scheduling constraints that should be used when managing lifecycle of workloads from scheduling perspective, including scheduling, preemption, eviction and other phases.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object's metadata. Name must be a DNS subdomain." + }, + "spec": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.WorkloadSpec", + "description": "Spec defines the desired behavior of a Workload." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.WorkloadList": { + "description": "WorkloadList contains a list of Workload resources.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Workloads.", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "WorkloadList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.WorkloadSpec": { + "description": "WorkloadSpec defines the desired state of a Workload.", + "properties": { + "controllerRef": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.TypedLocalObjectReference", + "description": "ControllerRef is an optional reference to the controlling object, such as a Deployment or Job. This field is intended for use by tools like CLIs to provide a link back to the original workload definition. When set, it cannot be changed." + }, + "podGroups": { + "description": "PodGroups is the list of pod groups that make up the Workload. The maximum number of pod groups is 8. This field is immutable.", + "items": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.PodGroup" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + } + }, + "required": [ + "podGroups" + ], + "type": "object" + }, "io.k8s.api.storage.v1.CSIDriver": { "description": "CSIDriver captures information about a Container Storage Interface (CSI) volume driver deployed on the cluster. Kubernetes attach detach controller uses this object to determine whether attach is required. Kubelet uses this object to determine whether pod information needs to be passed on mount. CSIDriver objects are non-namespaced.", "properties": { @@ -19101,6 +19308,10 @@ "description": "seLinuxMount specifies if the CSI driver supports \"-o context\" mount option.\n\nWhen \"true\", the CSI driver must ensure that all volumes provided by this CSI driver can be mounted separately with different `-o context` options. This is typical for storage backends that provide volumes as filesystems on block devices or as independent shared volumes. Kubernetes will call NodeStage / NodePublish with \"-o context=xyz\" mount option when mounting a ReadWriteOncePod volume used in Pod that has explicitly set SELinux context. In the future, it may be expanded to other volume AccessModes. In any case, Kubernetes will ensure that the volume is mounted only with a single SELinux context.\n\nWhen \"false\", Kubernetes won't pass any special SELinux mount options to the driver. This is typical for volumes that represent subdirectories of a bigger shared filesystem.\n\nDefault is \"false\".", "type": "boolean" }, + "serviceAccountTokenInSecrets": { + "description": "serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that service account tokens should be passed via the Secrets field in NodePublishVolumeRequest instead of the VolumeContext field. The CSI specification provides a dedicated Secrets field for sensitive information like tokens, which is the appropriate mechanism for handling credentials. This addresses security concerns where sensitive tokens were being logged as part of volume context.\n\nWhen \"true\", kubelet will pass the tokens only in the Secrets field with the key \"csi.storage.k8s.io/serviceAccount.tokens\". The CSI driver must be updated to read tokens from the Secrets field instead of VolumeContext.\n\nWhen \"false\" or not set, kubelet will pass the tokens in VolumeContext with the key \"csi.storage.k8s.io/serviceAccount.tokens\" (existing behavior). This maintains backward compatibility with existing CSI drivers.\n\nThis field can only be set when TokenRequests is configured. The API server will reject CSIDriver specs that set this field without TokenRequests.\n\nDefault behavior if unset is to pass tokens in the VolumeContext field.", + "type": "boolean" + }, "storageCapacity": { "description": "storageCapacity indicates that the CSI volume driver wants pod scheduling to consider the storage capacity that the driver deployment will report by creating CSIStorageCapacity objects with capacity information, if set to true.\n\nThe check can be enabled immediately when deploying a driver. In that case, provisioning new volumes with late binding will pause until the driver deployment has published some suitable CSIStorageCapacity object.\n\nAlternatively, the driver can be deployed with the field unset or false and it can be flipped later when storage capacity information has been published.\n\nThis field was immutable in Kubernetes <= 1.22 and now is mutable.", "type": "boolean" @@ -19682,80 +19893,6 @@ }, "type": "object" }, - "io.k8s.api.storage.v1alpha1.VolumeAttributesClass": { - "description": "VolumeAttributesClass represents a specification of mutable volume attributes defined by the CSI driver. The class can be specified during dynamic provisioning of PersistentVolumeClaims, and changed in the PersistentVolumeClaim spec after provisioning.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "driverName": { - "description": "Name of the CSI driver This field is immutable.", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "parameters": { - "additionalProperties": { - "type": "string" - }, - "description": "parameters hold volume attributes defined by the CSI driver. These values are opaque to the Kubernetes and are passed directly to the CSI driver. The underlying storage provider supports changing these attributes on an existing volume, however the parameters field itself is immutable. To invoke a volume update, a new VolumeAttributesClass should be created with new parameters, and the PersistentVolumeClaim should be updated to reference the new VolumeAttributesClass.\n\nThis field is required and must contain at least one key/value pair. The keys cannot be empty, and the maximum number of parameters is 512, with a cumulative max size of 256K. If the CSI driver rejects invalid parameters, the target PersistentVolumeClaim will be set to an \"Infeasible\" state in the modifyVolumeStatus field.", - "type": "object" - } - }, - "required": [ - "driverName" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.storage.v1alpha1.VolumeAttributesClassList": { - "description": "VolumeAttributesClassList is a collection of VolumeAttributesClass objects.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "items is the list of VolumeAttributesClass objects.", - "items": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - }, - "type": "array" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", - "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClassList", - "version": "v1alpha1" - } - ] - }, "io.k8s.api.storage.v1beta1.VolumeAttributesClass": { "description": "VolumeAttributesClass represents a specification of mutable volume attributes defined by the CSI driver. The class can be specified during dynamic provisioning of PersistentVolumeClaims, and changed in the PersistentVolumeClaim spec after provisioning.", "properties": { @@ -19830,55 +19967,7 @@ } ] }, - "io.k8s.api.storagemigration.v1alpha1.GroupVersionResource": { - "description": "The names of the group, the version, and the resource.", - "properties": { - "group": { - "description": "The name of the group.", - "type": "string" - }, - "resource": { - "description": "The name of the resource.", - "type": "string" - }, - "version": { - "description": "The name of the version.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.api.storagemigration.v1alpha1.MigrationCondition": { - "description": "Describes the state of a migration at a certain point.", - "properties": { - "lastUpdateTime": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", - "description": "The last time this condition was updated." - }, - "message": { - "description": "A human readable message indicating details about the transition.", - "type": "string" - }, - "reason": { - "description": "The reason for the condition's last transition.", - "type": "string" - }, - "status": { - "description": "Status of the condition, one of True, False, Unknown.", - "type": "string" - }, - "type": { - "description": "Type of the condition.", - "type": "string" - } - }, - "required": [ - "type", - "status" - ], - "type": "object" - }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration": { + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigration": { "description": "StorageVersionMigration represents a migration of stored data to the latest storage version.", "properties": { "apiVersion": { @@ -19894,11 +19983,11 @@ "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" }, "spec": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationSpec", + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationSpec", "description": "Specification of the migration." }, "status": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationStatus", + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationStatus", "description": "Status of the migration." } }, @@ -19907,11 +19996,11 @@ { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } ] }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList": { + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList": { "description": "StorageVersionMigrationList is a collection of storage version migrations.", "properties": { "apiVersion": { @@ -19921,15 +20010,9 @@ "items": { "description": "Items is the list of StorageVersionMigration", "items": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "type" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge" + "type": "array" }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -19948,19 +20031,15 @@ { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigrationList", - "version": "v1alpha1" + "version": "v1beta1" } ] }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationSpec": { + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationSpec": { "description": "Spec of the storage version migration.", "properties": { - "continueToken": { - "description": "The token used in the list options to get the next chunk of objects to migrate. When the .status.conditions indicates the migration is \"Running\", users can use this token to check the progress of the migration.", - "type": "string" - }, "resource": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.GroupVersionResource", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.GroupResource", "description": "The resource that is being migrated. The migrator sends requests to the endpoint serving the resource. Immutable." } }, @@ -19969,13 +20048,13 @@ ], "type": "object" }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationStatus": { + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationStatus": { "description": "Status of the storage version migration.", "properties": { "conditions": { "description": "The latest available observations of the migration's current state.", "items": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.MigrationCondition" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" }, "type": "array", "x-kubernetes-list-map-keys": [ @@ -20092,6 +20171,11 @@ "description": "message is a human-readable message indicating details about last transition.", "type": "string" }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, "reason": { "description": "reason is a unique, one-word, CamelCase reason for the condition's last transition.", "type": "string" @@ -20246,6 +20330,11 @@ ], "x-kubernetes-list-type": "map" }, + "observedGeneration": { + "description": "The generation observed by the CRD controller.", + "format": "int64", + "type": "integer" + }, "storedVersions": { "description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.", "items": { @@ -21316,7 +21405,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -21350,6 +21439,22 @@ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", "type": "object" }, + "io.k8s.apimachinery.pkg.apis.meta.v1.GroupResource": { + "description": "GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + "properties": { + "group": { + "type": "string" + }, + "resource": { + "type": "string" + } + }, + "required": [ + "group", + "resource" + ], + "type": "object" + }, "io.k8s.apimachinery.pkg.apis.meta.v1.GroupVersionForDiscovery": { "description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", "properties": { @@ -21657,8 +21762,7 @@ }, "details": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails", - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -22073,7 +22177,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -58432,13 +58536,208 @@ } } }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests": { + "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1alpha1ClusterTrustBundleList", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "certificates_v1alpha1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles/{name}": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1alpha1ClusterTrustBundle", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "certificates_v1alpha1" + ], + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the ClusterTrustBundle", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/": { + "get": { + "consumes": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "description": "get available resources", + "operationId": "getCertificatesV1beta1APIResources", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "certificates_v1beta1" + ] + } + }, + "/apis/certificates.k8s.io/v1beta1/clustertrustbundles": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of PodCertificateRequest", - "operationId": "deleteCertificatesV1alpha1CollectionNamespacedPodCertificateRequest", + "description": "delete collection of ClusterTrustBundle", + "operationId": "deleteCertificatesV1beta1CollectionClusterTrustBundle", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -58508,21 +58807,21 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } }, "get": { "consumes": [ "*/*" ], - "description": "list or watch objects of kind PodCertificateRequest", - "operationId": "listCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "list or watch objects of kind ClusterTrustBundle", + "operationId": "listCertificatesV1beta1ClusterTrustBundle", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -58568,7 +58867,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundleList" } }, "401": { @@ -58579,19 +58878,16 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } }, "parameters": [ - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -58600,15 +58896,15 @@ "consumes": [ "*/*" ], - "description": "create a PodCertificateRequest", - "operationId": "createCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "create a ClusterTrustBundle", + "operationId": "createCertificatesV1beta1ClusterTrustBundle", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, { @@ -58639,19 +58935,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "401": { @@ -58662,23 +58958,23 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } } }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests/{name}": { + "/apis/certificates.k8s.io/v1beta1/clustertrustbundles/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete a PodCertificateRequest", - "operationId": "deleteCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "delete a ClusterTrustBundle", + "operationId": "deleteCertificatesV1beta1ClusterTrustBundle", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -58730,21 +59026,21 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } }, "get": { "consumes": [ "*/*" ], - "description": "read the specified PodCertificateRequest", - "operationId": "readCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "read the specified ClusterTrustBundle", + "operationId": "readCertificatesV1beta1ClusterTrustBundle", "produces": [ "application/json", "application/yaml", @@ -58755,7 +59051,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "401": { @@ -58766,27 +59062,24 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } }, "parameters": [ { - "description": "name of the PodCertificateRequest", + "description": "name of the ClusterTrustBundle", "in": "path", "name": "name", "required": true, "type": "string", "uniqueItems": true }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -58799,8 +59092,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified PodCertificateRequest", - "operationId": "patchCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "partially update the specified ClusterTrustBundle", + "operationId": "patchCertificatesV1beta1ClusterTrustBundle", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -58836,13 +59129,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "401": { @@ -58853,28 +59146,28 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } }, "put": { "consumes": [ "*/*" ], - "description": "replace the specified PodCertificateRequest", - "operationId": "replaceCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "replace the specified ClusterTrustBundle", + "operationId": "replaceCertificatesV1beta1ClusterTrustBundle", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, { @@ -58905,13 +59198,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" } }, "401": { @@ -58922,23 +59215,71 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" + "kind": "ClusterTrustBundle", + "version": "v1beta1" } } }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests/{name}/status": { - "get": { + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests": { + "delete": { "consumes": [ "*/*" ], - "description": "read status of the specified PodCertificateRequest", - "operationId": "readCertificatesV1alpha1NamespacedPodCertificateRequestStatus", + "description": "delete collection of PodCertificateRequest", + "operationId": "deleteCertificatesV1beta1CollectionNamespacedPodCertificateRequest", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + } + ], "produces": [ "application/json", "application/yaml", @@ -58949,7 +59290,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "401": { @@ -58960,83 +59301,67 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "get", + "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } }, - "parameters": [ - { - "description": "name of the PodCertificateRequest", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - } - ], - "patch": { + "get": { "consumes": [ - "application/json-patch+json", - "application/merge-patch+json", - "application/strategic-merge-patch+json", - "application/apply-patch+yaml", - "application/apply-patch+cbor" + "*/*" ], - "description": "partially update status of the specified PodCertificateRequest", - "operationId": "patchCertificatesV1alpha1NamespacedPodCertificateRequestStatus", + "description": "list or watch objects of kind PodCertificateRequest", + "operationId": "listCertificatesV1beta1NamespacedPodCertificateRequest", "parameters": [ { - "$ref": "#/parameters/body-78PwaGsr" + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" }, { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/continue-QfD61s0i" }, { - "$ref": "#/parameters/fieldManager-7c6nTn1T" + "$ref": "#/parameters/fieldSelector-xIcQKXFG" }, { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/labelSelector-5Zw57w4C" }, { - "$ref": "#/parameters/force-tOGGb0Yi" + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" } }, "401": { @@ -59047,28 +59372,36 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "patch", + "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } }, - "put": { + "parameters": [ + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + } + ], + "post": { "consumes": [ "*/*" ], - "description": "replace status of the specified PodCertificateRequest", - "operationId": "replaceCertificatesV1alpha1NamespacedPodCertificateRequestStatus", + "description": "create a PodCertificateRequest", + "operationId": "createCertificatesV1beta1NamespacedPodCertificateRequest", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, { @@ -59099,13 +59432,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59116,37 +59455,64 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "put", + "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } } }, - "/apis/certificates.k8s.io/v1alpha1/podcertificaterequests": { - "get": { + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests/{name}": { + "delete": { "consumes": [ "*/*" ], - "description": "list or watch objects of kind PodCertificateRequest", - "operationId": "listCertificatesV1alpha1PodCertificateRequestForAllNamespaces", + "description": "delete a PodCertificateRequest", + "operationId": "deleteCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + } + ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "401": { @@ -59157,72 +59523,32 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "list", + "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles": { "get": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1ClusterTrustBundleList", + "description": "read the specified PodCertificateRequest", + "operationId": "readCertificatesV1beta1NamespacedPodCertificateRequest", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59233,72 +59559,83 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1alpha1" + "kind": "PodCertificateRequest", + "version": "v1beta1" } }, "parameters": [ { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" + "description": "name of the PodCertificateRequest", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true }, { - "$ref": "#/parameters/limit-1NfNmdNH" + "$ref": "#/parameters/namespace-vgWSWtn3" }, { "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles/{name}": { - "get": { + ], + "patch": { "consumes": [ - "*/*" + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" + ], + "description": "partially update the specified PodCertificateRequest", + "operationId": "patchCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "$ref": "#/parameters/body-78PwaGsr" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-7c6nTn1T" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/force-tOGGb0Yi" + } ], - "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1alpha1ClusterTrustBundle", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59309,80 +59646,65 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "watch", + "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1alpha1" + "kind": "PodCertificateRequest", + "version": "v1beta1" } }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "description": "name of the ClusterTrustBundle", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/namespaces/{namespace}/podcertificaterequests": { - "get": { + "put": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1NamespacedPodCertificateRequestList", + "description": "replace the specified PodCertificateRequest", + "operationId": "replaceCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-Qy4HdaTW" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + } + ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59393,75 +59715,34 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" + "version": "v1beta1" } - ] + } }, - "/apis/certificates.k8s.io/v1alpha1/watch/namespaces/{namespace}/podcertificaterequests/{name}": { + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests/{name}/status": { "get": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "read status of the specified PodCertificateRequest", + "operationId": "readCertificatesV1beta1NamespacedPodCertificateRequestStatus", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59472,31 +59753,16 @@ "https" ], "tags": [ - "certificates_v1alpha1" + "certificates_v1beta1" ], - "x-kubernetes-action": "watch", + "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "PodCertificateRequest", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, { "description": "name of the PodCertificateRequest", "in": "path", @@ -59510,148 +59776,21 @@ }, { "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/podcertificaterequests": { - "get": { - "consumes": [ - "*/*" - ], - "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1PodCertificateRequestListForAllNamespaces", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" } - ] - }, - "/apis/certificates.k8s.io/v1beta1/": { - "get": { - "consumes": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" - ], - "description": "get available resources", - "operationId": "getCertificatesV1beta1APIResources", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "certificates_v1beta1" - ] - } - }, - "/apis/certificates.k8s.io/v1beta1/clustertrustbundles": { - "delete": { + ], + "patch": { "consumes": [ - "*/*" + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" ], - "description": "delete collection of ClusterTrustBundle", - "operationId": "deleteCertificatesV1beta1CollectionClusterTrustBundle", + "description": "partially update status of the specified PodCertificateRequest", + "operationId": "patchCertificatesV1beta1NamespacedPodCertificateRequestStatus", "parameters": [ { - "$ref": "#/parameters/body-2Y1dVQaQ" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" + "$ref": "#/parameters/body-78PwaGsr" }, { "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", @@ -59661,37 +59800,17 @@ "uniqueItems": true }, { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" - }, - { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + "$ref": "#/parameters/fieldManager-7c6nTn1T" }, { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true }, { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + "$ref": "#/parameters/force-tOGGb0Yi" } ], "produces": [ @@ -59704,78 +59823,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "certificates_v1beta1" - ], - "x-kubernetes-action": "deletecollection", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1beta1" - } - }, - "get": { - "consumes": [ - "*/*" - ], - "description": "list or watch objects of kind ClusterTrustBundle", - "operationId": "listCertificatesV1beta1ClusterTrustBundle", - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ], - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" - ], - "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundleList" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59788,31 +59842,26 @@ "tags": [ "certificates_v1beta1" ], - "x-kubernetes-action": "list", + "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, - "parameters": [ - { - "$ref": "#/parameters/pretty-tJGM1-ng" - } - ], - "post": { + "put": { "consumes": [ "*/*" ], - "description": "create a ClusterTrustBundle", - "operationId": "createCertificatesV1beta1ClusterTrustBundle", + "description": "replace status of the specified PodCertificateRequest", + "operationId": "replaceCertificatesV1beta1NamespacedPodCertificateRequestStatus", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, { @@ -59843,19 +59892,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequest" } }, "401": { @@ -59868,62 +59911,35 @@ "tags": [ "certificates_v1beta1" ], - "x-kubernetes-action": "post", + "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } } }, - "/apis/certificates.k8s.io/v1beta1/clustertrustbundles/{name}": { - "delete": { + "/apis/certificates.k8s.io/v1beta1/podcertificaterequests": { + "get": { "consumes": [ "*/*" ], - "description": "delete a ClusterTrustBundle", - "operationId": "deleteCertificatesV1beta1ClusterTrustBundle", - "parameters": [ - { - "$ref": "#/parameters/body-2Y1dVQaQ" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" - }, - { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" - } - ], + "description": "list or watch objects of kind PodCertificateRequest", + "operationId": "listCertificatesV1beta1PodCertificateRequestForAllNamespaces", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" } }, "401": { @@ -59936,30 +59952,70 @@ "tags": [ "certificates_v1beta1" ], - "x-kubernetes-action": "delete", + "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles": { "get": { "consumes": [ "*/*" ], - "description": "read the specified ClusterTrustBundle", - "operationId": "readCertificatesV1beta1ClusterTrustBundle", + "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1ClusterTrustBundleList", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -59972,7 +60028,7 @@ "tags": [ "certificates_v1beta1" ], - "x-kubernetes-action": "get", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "ClusterTrustBundle", @@ -59981,69 +60037,61 @@ }, "parameters": [ { - "description": "name of the ClusterTrustBundle", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" }, { "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } - ], - "patch": { + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles/{name}": { + "get": { "consumes": [ - "application/json-patch+json", - "application/merge-patch+json", - "application/strategic-merge-patch+json", - "application/apply-patch+yaml", - "application/apply-patch+cbor" - ], - "description": "partially update the specified ClusterTrustBundle", - "operationId": "patchCertificatesV1beta1ClusterTrustBundle", - "parameters": [ - { - "$ref": "#/parameters/body-78PwaGsr" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-7c6nTn1T" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/force-tOGGb0Yi" - } + "*/*" ], + "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1beta1ClusterTrustBundle", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -60056,90 +60104,64 @@ "tags": [ "certificates_v1beta1" ], - "x-kubernetes-action": "patch", + "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", "kind": "ClusterTrustBundle", "version": "v1beta1" } }, - "put": { - "consumes": [ - "*/*" - ], - "description": "replace the specified ClusterTrustBundle", - "operationId": "replaceCertificatesV1beta1ClusterTrustBundle", - "parameters": [ - { - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-Qy4HdaTW" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - } - ], - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.certificates.v1beta1.ClusterTrustBundle" - } - }, - "401": { - "description": "Unauthorized" - } + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" }, - "schemes": [ - "https" - ], - "tags": [ - "certificates_v1beta1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1beta1" + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the ClusterTrustBundle", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } - } + ] }, - "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles": { + "/apis/certificates.k8s.io/v1beta1/watch/namespaces/{namespace}/podcertificaterequests": { "get": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1beta1ClusterTrustBundleList", + "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1NamespacedPodCertificateRequestList", "produces": [ "application/json", "application/yaml", @@ -60169,7 +60191,7 @@ "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, @@ -60189,6 +60211,9 @@ { "$ref": "#/parameters/limit-1NfNmdNH" }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" }, @@ -60209,13 +60234,13 @@ } ] }, - "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles/{name}": { + "/apis/certificates.k8s.io/v1beta1/watch/namespaces/{namespace}/podcertificaterequests/{name}": { "get": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1beta1ClusterTrustBundle", + "description": "watch changes to an object of kind PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1beta1NamespacedPodCertificateRequest", "produces": [ "application/json", "application/yaml", @@ -60245,7 +60270,7 @@ "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, @@ -60266,13 +60291,92 @@ "$ref": "#/parameters/limit-1NfNmdNH" }, { - "description": "name of the ClusterTrustBundle", + "description": "name of the PodCertificateRequest", "in": "path", "name": "name", "required": true, "type": "string", "uniqueItems": true }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/podcertificaterequests": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1PodCertificateRequestListForAllNamespaces", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" }, @@ -81462,6 +81566,197 @@ } } }, + "/apis/resource.k8s.io/v1alpha3/devicetaintrules/{name}/status": { + "get": { + "consumes": [ + "*/*" + ], + "description": "read status of the specified DeviceTaintRule", + "operationId": "readResourceV1alpha3DeviceTaintRuleStatus", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + }, + "parameters": [ + { + "description": "name of the DeviceTaintRule", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + } + ], + "patch": { + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" + ], + "description": "partially update status of the specified DeviceTaintRule", + "operationId": "patchResourceV1alpha3DeviceTaintRuleStatus", + "parameters": [ + { + "$ref": "#/parameters/body-78PwaGsr" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-7c6nTn1T" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/force-tOGGb0Yi" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + }, + "put": { + "consumes": [ + "*/*" + ], + "description": "replace status of the specified DeviceTaintRule", + "operationId": "replaceResourceV1alpha3DeviceTaintRuleStatus", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-Qy4HdaTW" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + } + }, "/apis/resource.k8s.io/v1alpha3/watch/devicetaintrules": { "get": { "consumes": [ @@ -88690,40 +88985,7 @@ } ] }, - "/apis/storage.k8s.io/": { - "get": { - "consumes": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf" - ], - "description": "get information of a group", - "operationId": "getStorageAPIGroup", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" - } - }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "storage" - ] - } - }, - "/apis/storage.k8s.io/v1/": { + "/apis/scheduling.k8s.io/v1alpha1/": { "get": { "consumes": [ "application/json", @@ -88732,7 +88994,7 @@ "application/cbor" ], "description": "get available resources", - "operationId": "getStorageV1APIResources", + "operationId": "getSchedulingV1alpha1APIResources", "produces": [ "application/json", "application/yaml", @@ -88754,17 +89016,17 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ] } }, - "/apis/storage.k8s.io/v1/csidrivers": { + "/apis/scheduling.k8s.io/v1alpha1/namespaces/{namespace}/workloads": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of CSIDriver", - "operationId": "deleteStorageV1CollectionCSIDriver", + "description": "delete collection of Workload", + "operationId": "deleteSchedulingV1alpha1CollectionNamespacedWorkload", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -88834,21 +89096,21 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ], "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" } }, "get": { "consumes": [ "*/*" ], - "description": "list or watch objects of kind CSIDriver", - "operationId": "listStorageV1CSIDriver", + "description": "list or watch objects of kind Workload", + "operationId": "listSchedulingV1alpha1NamespacedWorkload", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -88894,7 +89156,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriverList" + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.WorkloadList" } }, "401": { @@ -88905,16 +89167,19 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ], "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" } }, "parameters": [ + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -88923,15 +89188,281 @@ "consumes": [ "*/*" ], - "description": "create a CSIDriver", - "operationId": "createStorageV1CSIDriver", + "description": "create a Workload", + "operationId": "createSchedulingV1alpha1NamespacedWorkload", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-Qy4HdaTW" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + } + }, + "/apis/scheduling.k8s.io/v1alpha1/namespaces/{namespace}/workloads/{name}": { + "delete": { + "consumes": [ + "*/*" + ], + "description": "delete a Workload", + "operationId": "deleteSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "get": { + "consumes": [ + "*/*" + ], + "description": "read the specified Workload", + "operationId": "readSchedulingV1alpha1NamespacedWorkload", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "name of the Workload", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + } + ], + "patch": { + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" + ], + "description": "partially update the specified Workload", + "operationId": "patchSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "$ref": "#/parameters/body-78PwaGsr" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-7c6nTn1T" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/force-tOGGb0Yi" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "put": { + "consumes": [ + "*/*" + ], + "description": "replace the specified Workload", + "operationId": "replaceSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" } }, { @@ -88956,25 +89487,139 @@ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + } + }, + "/apis/scheduling.k8s.io/v1alpha1/watch/namespaces/{namespace}/workloads": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch individual changes to a list of Workload. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchSchedulingV1alpha1NamespacedWorkloadList", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/scheduling.k8s.io/v1alpha1/watch/namespaces/{namespace}/workloads/{name}": { + "get": { + "consumes": [ + "*/*" + ], + "description": "watch changes to an object of kind Workload. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchSchedulingV1alpha1NamespacedWorkload", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -88985,64 +89630,83 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ], - "x-kubernetes-action": "post", + "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" } - } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the Workload", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] }, - "/apis/storage.k8s.io/v1/csidrivers/{name}": { - "delete": { + "/apis/scheduling.k8s.io/v1alpha1/watch/workloads": { + "get": { "consumes": [ "*/*" ], - "description": "delete a CSIDriver", - "operationId": "deleteStorageV1CSIDriver", - "parameters": [ - { - "$ref": "#/parameters/body-2Y1dVQaQ" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" - }, - { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" - } - ], + "description": "watch individual changes to a list of Workload. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchSchedulingV1alpha1WorkloadListForAllNamespaces", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -89053,32 +89717,72 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ], - "x-kubernetes-action": "delete", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" } }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/scheduling.k8s.io/v1alpha1/workloads": { "get": { "consumes": [ "*/*" ], - "description": "read the specified CSIDriver", - "operationId": "readStorageV1CSIDriver", + "description": "list or watch objects of kind Workload", + "operationId": "listSchedulingV1alpha1WorkloadForAllNamespaces", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.api.scheduling.v1alpha1.WorkloadList" } }, "401": { @@ -89089,80 +89793,70 @@ "https" ], "tags": [ - "storage_v1" + "scheduling_v1alpha1" ], - "x-kubernetes-action": "get", + "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" } }, "parameters": [ { - "description": "name of the CSIDriver", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" }, { "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } - ], - "patch": { + ] + }, + "/apis/storage.k8s.io/": { + "get": { "consumes": [ - "application/json-patch+json", - "application/merge-patch+json", - "application/strategic-merge-patch+json", - "application/apply-patch+yaml", - "application/apply-patch+cbor" - ], - "description": "partially update the specified CSIDriver", - "operationId": "patchStorageV1CSIDriver", - "parameters": [ - { - "$ref": "#/parameters/body-78PwaGsr" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-7c6nTn1T" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/force-tOGGb0Yi" - } + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" ], + "description": "get information of a group", + "operationId": "getStorageAPIGroup", "produces": [ "application/json", "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/vnd.kubernetes.protobuf" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" } }, "401": { @@ -89173,48 +89867,20 @@ "https" ], "tags": [ - "storage_v1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" - } - }, - "put": { + "storage" + ] + } + }, + "/apis/storage.k8s.io/v1/": { + "get": { "consumes": [ - "*/*" - ], - "description": "replace the specified CSIDriver", - "operationId": "replaceStorageV1CSIDriver", - "parameters": [ - { - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-Qy4HdaTW" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - } + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor" ], + "description": "get available resources", + "operationId": "getStorageV1APIResources", "produces": [ "application/json", "application/yaml", @@ -89225,13 +89891,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" } }, "401": { @@ -89243,22 +89903,16 @@ ], "tags": [ "storage_v1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" - } + ] } }, - "/apis/storage.k8s.io/v1/csinodes": { + "/apis/storage.k8s.io/v1/csidrivers": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of CSINode", - "operationId": "deleteStorageV1CollectionCSINode", + "description": "delete collection of CSIDriver", + "operationId": "deleteStorageV1CollectionCSIDriver", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -89333,7 +89987,7 @@ "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } }, @@ -89341,8 +89995,8 @@ "consumes": [ "*/*" ], - "description": "list or watch objects of kind CSINode", - "operationId": "listStorageV1CSINode", + "description": "list or watch objects of kind CSIDriver", + "operationId": "listStorageV1CSIDriver", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -89388,7 +90042,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINodeList" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriverList" } }, "401": { @@ -89404,7 +90058,7 @@ "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } }, @@ -89417,15 +90071,15 @@ "consumes": [ "*/*" ], - "description": "create a CSINode", - "operationId": "createStorageV1CSINode", + "description": "create a CSIDriver", + "operationId": "createStorageV1CSIDriver", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, { @@ -89456,19 +90110,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "401": { @@ -89484,18 +90138,18 @@ "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/csinodes/{name}": { + "/apis/storage.k8s.io/v1/csidrivers/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete a CSINode", - "operationId": "deleteStorageV1CSINode", + "description": "delete a CSIDriver", + "operationId": "deleteStorageV1CSIDriver", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -89530,13 +90184,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "401": { @@ -89552,7 +90206,7 @@ "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } }, @@ -89560,8 +90214,8 @@ "consumes": [ "*/*" ], - "description": "read the specified CSINode", - "operationId": "readStorageV1CSINode", + "description": "read the specified CSIDriver", + "operationId": "readStorageV1CSIDriver", "produces": [ "application/json", "application/yaml", @@ -89572,7 +90226,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "401": { @@ -89588,13 +90242,13 @@ "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } }, "parameters": [ { - "description": "name of the CSINode", + "description": "name of the CSIDriver", "in": "path", "name": "name", "required": true, @@ -89613,8 +90267,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified CSINode", - "operationId": "patchStorageV1CSINode", + "description": "partially update the specified CSIDriver", + "operationId": "patchStorageV1CSIDriver", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -89650,13 +90304,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "401": { @@ -89672,7 +90326,7 @@ "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } }, @@ -89680,15 +90334,15 @@ "consumes": [ "*/*" ], - "description": "replace the specified CSINode", - "operationId": "replaceStorageV1CSINode", + "description": "replace the specified CSIDriver", + "operationId": "replaceStorageV1CSIDriver", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, { @@ -89719,13 +90373,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIDriver" } }, "401": { @@ -89741,94 +90395,18 @@ "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "CSIDriver", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/csistoragecapacities": { - "get": { - "consumes": [ - "*/*" - ], - "description": "list or watch objects of kind CSIStorageCapacity", - "operationId": "listStorageV1CSIStorageCapacityForAllNamespaces", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacityList" - } - }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "storage_v1" - ], - "x-kubernetes-action": "list", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", - "version": "v1" - } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities": { + "/apis/storage.k8s.io/v1/csinodes": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of CSIStorageCapacity", - "operationId": "deleteStorageV1CollectionNamespacedCSIStorageCapacity", + "description": "delete collection of CSINode", + "operationId": "deleteStorageV1CollectionCSINode", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -89903,7 +90481,7 @@ "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } }, @@ -89911,8 +90489,8 @@ "consumes": [ "*/*" ], - "description": "list or watch objects of kind CSIStorageCapacity", - "operationId": "listStorageV1NamespacedCSIStorageCapacity", + "description": "list or watch objects of kind CSINode", + "operationId": "listStorageV1CSINode", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -89958,7 +90536,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacityList" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINodeList" } }, "401": { @@ -89974,14 +90552,11 @@ "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } }, "parameters": [ - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -89990,15 +90565,15 @@ "consumes": [ "*/*" ], - "description": "create a CSIStorageCapacity", - "operationId": "createStorageV1NamespacedCSIStorageCapacity", + "description": "create a CSINode", + "operationId": "createStorageV1CSINode", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, { @@ -90029,19 +90604,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "401": { @@ -90057,18 +90632,18 @@ "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}": { + "/apis/storage.k8s.io/v1/csinodes/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete a CSIStorageCapacity", - "operationId": "deleteStorageV1NamespacedCSIStorageCapacity", + "description": "delete a CSINode", + "operationId": "deleteStorageV1CSINode", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -90103,13 +90678,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "401": { @@ -90125,7 +90700,7 @@ "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } }, @@ -90133,8 +90708,8 @@ "consumes": [ "*/*" ], - "description": "read the specified CSIStorageCapacity", - "operationId": "readStorageV1NamespacedCSIStorageCapacity", + "description": "read the specified CSINode", + "operationId": "readStorageV1CSINode", "produces": [ "application/json", "application/yaml", @@ -90145,7 +90720,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "401": { @@ -90161,22 +90736,19 @@ "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } }, "parameters": [ { - "description": "name of the CSIStorageCapacity", + "description": "name of the CSINode", "in": "path", "name": "name", "required": true, "type": "string", "uniqueItems": true }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -90189,8 +90761,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified CSIStorageCapacity", - "operationId": "patchStorageV1NamespacedCSIStorageCapacity", + "description": "partially update the specified CSINode", + "operationId": "patchStorageV1CSINode", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -90226,13 +90798,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "401": { @@ -90248,7 +90820,7 @@ "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } }, @@ -90256,15 +90828,15 @@ "consumes": [ "*/*" ], - "description": "replace the specified CSIStorageCapacity", - "operationId": "replaceStorageV1NamespacedCSIStorageCapacity", + "description": "replace the specified CSINode", + "operationId": "replaceStorageV1CSINode", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, { @@ -90295,13 +90867,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSINode" } }, "401": { @@ -90317,18 +90889,94 @@ "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "CSINode", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/storageclasses": { + "/apis/storage.k8s.io/v1/csistoragecapacities": { + "get": { + "consumes": [ + "*/*" + ], + "description": "list or watch objects of kind CSIStorageCapacity", + "operationId": "listStorageV1CSIStorageCapacityForAllNamespaces", + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacityList" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "schemes": [ + "https" + ], + "tags": [ + "storage_v1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "storage.k8s.io", + "kind": "CSIStorageCapacity", + "version": "v1" + } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of StorageClass", - "operationId": "deleteStorageV1CollectionStorageClass", + "description": "delete collection of CSIStorageCapacity", + "operationId": "deleteStorageV1CollectionNamespacedCSIStorageCapacity", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -90403,7 +91051,7 @@ "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } }, @@ -90411,8 +91059,8 @@ "consumes": [ "*/*" ], - "description": "list or watch objects of kind StorageClass", - "operationId": "listStorageV1StorageClass", + "description": "list or watch objects of kind CSIStorageCapacity", + "operationId": "listStorageV1NamespacedCSIStorageCapacity", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -90458,7 +91106,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClassList" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacityList" } }, "401": { @@ -90474,11 +91122,14 @@ "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } }, "parameters": [ + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -90487,15 +91138,15 @@ "consumes": [ "*/*" ], - "description": "create a StorageClass", - "operationId": "createStorageV1StorageClass", + "description": "create a CSIStorageCapacity", + "operationId": "createStorageV1NamespacedCSIStorageCapacity", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, { @@ -90526,19 +91177,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "401": { @@ -90554,18 +91205,18 @@ "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/storageclasses/{name}": { + "/apis/storage.k8s.io/v1/namespaces/{namespace}/csistoragecapacities/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete a StorageClass", - "operationId": "deleteStorageV1StorageClass", + "description": "delete a CSIStorageCapacity", + "operationId": "deleteStorageV1NamespacedCSIStorageCapacity", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -90600,13 +91251,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "401": { @@ -90622,7 +91273,7 @@ "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } }, @@ -90630,8 +91281,8 @@ "consumes": [ "*/*" ], - "description": "read the specified StorageClass", - "operationId": "readStorageV1StorageClass", + "description": "read the specified CSIStorageCapacity", + "operationId": "readStorageV1NamespacedCSIStorageCapacity", "produces": [ "application/json", "application/yaml", @@ -90642,7 +91293,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "401": { @@ -90658,19 +91309,22 @@ "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } }, "parameters": [ { - "description": "name of the StorageClass", + "description": "name of the CSIStorageCapacity", "in": "path", "name": "name", "required": true, "type": "string", "uniqueItems": true }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" } @@ -90683,8 +91337,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified StorageClass", - "operationId": "patchStorageV1StorageClass", + "description": "partially update the specified CSIStorageCapacity", + "operationId": "patchStorageV1NamespacedCSIStorageCapacity", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -90720,13 +91374,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "401": { @@ -90742,7 +91396,7 @@ "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } }, @@ -90750,15 +91404,15 @@ "consumes": [ "*/*" ], - "description": "replace the specified StorageClass", - "operationId": "replaceStorageV1StorageClass", + "description": "replace the specified CSIStorageCapacity", + "operationId": "replaceStorageV1NamespacedCSIStorageCapacity", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, { @@ -90789,13 +91443,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.CSIStorageCapacity" } }, "401": { @@ -90811,18 +91465,18 @@ "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "CSIStorageCapacity", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/volumeattachments": { + "/apis/storage.k8s.io/v1/storageclasses": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of VolumeAttachment", - "operationId": "deleteStorageV1CollectionVolumeAttachment", + "description": "delete collection of StorageClass", + "operationId": "deleteStorageV1CollectionStorageClass", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -90897,7 +91551,7 @@ "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } }, @@ -90905,8 +91559,8 @@ "consumes": [ "*/*" ], - "description": "list or watch objects of kind VolumeAttachment", - "operationId": "listStorageV1VolumeAttachment", + "description": "list or watch objects of kind StorageClass", + "operationId": "listStorageV1StorageClass", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -90952,7 +91606,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentList" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClassList" } }, "401": { @@ -90968,7 +91622,7 @@ "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } }, @@ -90981,15 +91635,15 @@ "consumes": [ "*/*" ], - "description": "create a VolumeAttachment", - "operationId": "createStorageV1VolumeAttachment", + "description": "create a StorageClass", + "operationId": "createStorageV1StorageClass", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, { @@ -91020,19 +91674,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "401": { @@ -91048,18 +91702,18 @@ "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/volumeattachments/{name}": { + "/apis/storage.k8s.io/v1/storageclasses/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete a VolumeAttachment", - "operationId": "deleteStorageV1VolumeAttachment", + "description": "delete a StorageClass", + "operationId": "deleteStorageV1StorageClass", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -91094,13 +91748,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "401": { @@ -91116,7 +91770,7 @@ "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } }, @@ -91124,8 +91778,8 @@ "consumes": [ "*/*" ], - "description": "read the specified VolumeAttachment", - "operationId": "readStorageV1VolumeAttachment", + "description": "read the specified StorageClass", + "operationId": "readStorageV1StorageClass", "produces": [ "application/json", "application/yaml", @@ -91136,7 +91790,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "401": { @@ -91152,13 +91806,13 @@ "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } }, "parameters": [ { - "description": "name of the VolumeAttachment", + "description": "name of the StorageClass", "in": "path", "name": "name", "required": true, @@ -91177,8 +91831,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified VolumeAttachment", - "operationId": "patchStorageV1VolumeAttachment", + "description": "partially update the specified StorageClass", + "operationId": "patchStorageV1StorageClass", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -91214,13 +91868,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "401": { @@ -91236,7 +91890,7 @@ "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } }, @@ -91244,15 +91898,15 @@ "consumes": [ "*/*" ], - "description": "replace the specified VolumeAttachment", - "operationId": "replaceStorageV1VolumeAttachment", + "description": "replace the specified StorageClass", + "operationId": "replaceStorageV1StorageClass", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, { @@ -91283,13 +91937,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.StorageClass" } }, "401": { @@ -91305,18 +91959,66 @@ "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "StorageClass", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/volumeattachments/{name}/status": { - "get": { + "/apis/storage.k8s.io/v1/volumeattachments": { + "delete": { "consumes": [ "*/*" ], - "description": "read status of the specified VolumeAttachment", - "operationId": "readStorageV1VolumeAttachmentStatus", + "description": "delete collection of VolumeAttachment", + "operationId": "deleteStorageV1CollectionVolumeAttachment", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + } + ], "produces": [ "application/json", "application/yaml", @@ -91327,7 +92029,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "401": { @@ -91340,78 +92042,65 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "get", + "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", "kind": "VolumeAttachment", "version": "v1" } }, - "parameters": [ - { - "description": "name of the VolumeAttachment", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - } - ], - "patch": { + "get": { "consumes": [ - "application/json-patch+json", - "application/merge-patch+json", - "application/strategic-merge-patch+json", - "application/apply-patch+yaml", - "application/apply-patch+cbor" + "*/*" ], - "description": "partially update status of the specified VolumeAttachment", - "operationId": "patchStorageV1VolumeAttachmentStatus", + "description": "list or watch objects of kind VolumeAttachment", + "operationId": "listStorageV1VolumeAttachment", "parameters": [ { - "$ref": "#/parameters/body-78PwaGsr" + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" }, { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/continue-QfD61s0i" }, { - "$ref": "#/parameters/fieldManager-7c6nTn1T" + "$ref": "#/parameters/fieldSelector-xIcQKXFG" }, { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true + "$ref": "#/parameters/labelSelector-5Zw57w4C" }, { - "$ref": "#/parameters/force-tOGGb0Yi" + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachmentList" } }, "401": { @@ -91424,19 +92113,24 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "patch", + "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", "kind": "VolumeAttachment", "version": "v1" } }, - "put": { + "parameters": [ + { + "$ref": "#/parameters/pretty-tJGM1-ng" + } + ], + "post": { "consumes": [ "*/*" ], - "description": "replace status of the specified VolumeAttachment", - "operationId": "replaceStorageV1VolumeAttachmentStatus", + "description": "create a VolumeAttachment", + "operationId": "createStorageV1VolumeAttachment", "parameters": [ { "in": "body", @@ -91483,6 +92177,12 @@ "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + } + }, "401": { "description": "Unauthorized" } @@ -91493,7 +92193,7 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "put", + "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", "kind": "VolumeAttachment", @@ -91501,20 +92201,17 @@ } } }, - "/apis/storage.k8s.io/v1/volumeattributesclasses": { + "/apis/storage.k8s.io/v1/volumeattachments/{name}": { "delete": { "consumes": [ "*/*" ], - "description": "delete collection of VolumeAttributesClass", - "operationId": "deleteStorageV1CollectionVolumeAttributesClass", + "description": "delete a VolumeAttachment", + "operationId": "deleteStorageV1VolumeAttachment", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, { "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", "in": "query", @@ -91522,38 +92219,17 @@ "type": "string", "uniqueItems": true }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, { "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" }, { "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, { "$ref": "#/parameters/orphanDependents-uRB25kX5" }, { "$ref": "#/parameters/propagationPolicy-6jk3prlO" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" } ], "produces": [ @@ -91566,7 +92242,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91579,10 +92261,10 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "deletecollection", + "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } }, @@ -91590,54 +92272,19 @@ "consumes": [ "*/*" ], - "description": "list or watch objects of kind VolumeAttributesClass", - "operationId": "listStorageV1VolumeAttributesClass", - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ], + "description": "read the specified VolumeAttachment", + "operationId": "readStorageV1VolumeAttachment", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClassList" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91650,32 +92297,39 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "list", + "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } }, "parameters": [ + { + "description": "name of the VolumeAttachment", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, { "$ref": "#/parameters/pretty-tJGM1-ng" } ], - "post": { + "patch": { "consumes": [ - "*/*" + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" ], - "description": "create a VolumeAttributesClass", - "operationId": "createStorageV1VolumeAttributesClass", + "description": "partially update the specified VolumeAttachment", + "operationId": "patchStorageV1VolumeAttachment", "parameters": [ { - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" - } + "$ref": "#/parameters/body-78PwaGsr" }, { "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", @@ -91685,7 +92339,7 @@ "uniqueItems": true }, { - "$ref": "#/parameters/fieldManager-Qy4HdaTW" + "$ref": "#/parameters/fieldManager-7c6nTn1T" }, { "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", @@ -91693,6 +92347,9 @@ "name": "fieldValidation", "type": "string", "uniqueItems": true + }, + { + "$ref": "#/parameters/force-tOGGb0Yi" } ], "produces": [ @@ -91705,19 +92362,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91730,24 +92381,27 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "post", + "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } - } - }, - "/apis/storage.k8s.io/v1/volumeattributesclasses/{name}": { - "delete": { + }, + "put": { "consumes": [ "*/*" ], - "description": "delete a VolumeAttributesClass", - "operationId": "deleteStorageV1VolumeAttributesClass", + "description": "replace the specified VolumeAttachment", + "operationId": "replaceStorageV1VolumeAttachment", "parameters": [ { - "$ref": "#/parameters/body-2Y1dVQaQ" + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" + } }, { "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", @@ -91757,16 +92411,14 @@ "uniqueItems": true }, { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" + "$ref": "#/parameters/fieldManager-Qy4HdaTW" }, { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true } ], "produces": [ @@ -91779,13 +92431,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, - "202": { - "description": "Accepted", + "201": { + "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91798,19 +92450,21 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "delete", + "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } - }, + } + }, + "/apis/storage.k8s.io/v1/volumeattachments/{name}/status": { "get": { "consumes": [ "*/*" ], - "description": "read the specified VolumeAttributesClass", - "operationId": "readStorageV1VolumeAttributesClass", + "description": "read status of the specified VolumeAttachment", + "operationId": "readStorageV1VolumeAttachmentStatus", "produces": [ "application/json", "application/yaml", @@ -91821,7 +92475,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91837,13 +92491,13 @@ "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } }, "parameters": [ { - "description": "name of the VolumeAttributesClass", + "description": "name of the VolumeAttachment", "in": "path", "name": "name", "required": true, @@ -91862,8 +92516,8 @@ "application/apply-patch+yaml", "application/apply-patch+cbor" ], - "description": "partially update the specified VolumeAttributesClass", - "operationId": "patchStorageV1VolumeAttributesClass", + "description": "partially update status of the specified VolumeAttachment", + "operationId": "patchStorageV1VolumeAttachmentStatus", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -91899,13 +92553,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91921,7 +92575,7 @@ "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } }, @@ -91929,15 +92583,15 @@ "consumes": [ "*/*" ], - "description": "replace the specified VolumeAttributesClass", - "operationId": "replaceStorageV1VolumeAttributesClass", + "description": "replace status of the specified VolumeAttachment", + "operationId": "replaceStorageV1VolumeAttachmentStatus", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, { @@ -91968,13 +92622,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttachment" } }, "401": { @@ -91990,108 +92644,77 @@ "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "VolumeAttachment", "version": "v1" } } }, - "/apis/storage.k8s.io/v1/watch/csidrivers": { - "get": { + "/apis/storage.k8s.io/v1/volumeattributesclasses": { + "delete": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of CSIDriver. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1CSIDriverList", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } + "description": "delete collection of VolumeAttributesClass", + "operationId": "deleteStorageV1CollectionVolumeAttributesClass", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" }, - "401": { - "description": "Unauthorized" + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" } - }, - "schemes": [ - "https" - ], - "tags": [ - "storage_v1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSIDriver", - "version": "v1" - } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/watch/csidrivers/{name}": { - "get": { - "consumes": [ - "*/*" ], - "description": "watch changes to an object of kind CSIDriver. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1CSIDriver", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" } }, "401": { @@ -92104,64 +92727,51 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watch", + "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIDriver", + "kind": "VolumeAttributesClass", "version": "v1" } }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "description": "name of the CSIDriver", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/watch/csinodes": { "get": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of CSINode. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1CSINodeList", + "description": "list or watch objects of kind VolumeAttributesClass", + "operationId": "listStorageV1VolumeAttributesClass", + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ], "produces": [ "application/json", "application/yaml", @@ -92175,7 +92785,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClassList" } }, "401": { @@ -92188,154 +92798,74 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSINode", + "kind": "VolumeAttributesClass", "version": "v1" } }, "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" } - ] - }, - "/apis/storage.k8s.io/v1/watch/csinodes/{name}": { - "get": { + ], + "post": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind CSINode. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1CSINode", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" - ], - "responses": { - "200": { - "description": "OK", + "description": "create a VolumeAttributesClass", + "operationId": "createStorageV1VolumeAttributesClass", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, - "401": { - "description": "Unauthorized" + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-Qy4HdaTW" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true } - }, - "schemes": [ - "https" - ], - "tags": [ - "storage_v1" - ], - "x-kubernetes-action": "watch", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "CSINode", - "version": "v1" - } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "description": "name of the CSINode", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/watch/csistoragecapacities": { - "get": { - "consumes": [ - "*/*" ], - "description": "watch individual changes to a list of CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1CSIStorageCapacityListForAllNamespaces", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, "401": { @@ -92348,70 +92878,62 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "VolumeAttributesClass", "version": "v1" } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] + } }, - "/apis/storage.k8s.io/v1/watch/namespaces/{namespace}/csistoragecapacities": { - "get": { + "/apis/storage.k8s.io/v1/volumeattributesclasses/{name}": { + "delete": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1NamespacedCSIStorageCapacityList", + "description": "delete a VolumeAttributesClass", + "operationId": "deleteStorageV1VolumeAttributesClass", + "parameters": [ + { + "$ref": "#/parameters/body-2Y1dVQaQ" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" + }, + { + "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" + }, + { + "$ref": "#/parameters/orphanDependents-uRB25kX5" + }, + { + "$ref": "#/parameters/propagationPolicy-6jk3prlO" + } + ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, "401": { @@ -92424,73 +92946,30 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "VolumeAttributesClass", "version": "v1" } }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/watch/namespaces/{namespace}/csistoragecapacities/{name}": { "get": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1NamespacedCSIStorageCapacity", + "description": "read the specified VolumeAttributesClass", + "operationId": "readStorageV1VolumeAttributesClass", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, "401": { @@ -92503,81 +92982,78 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watch", + "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "CSIStorageCapacity", + "kind": "VolumeAttributesClass", "version": "v1" } }, "parameters": [ { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "description": "name of the CSIStorageCapacity", + "description": "name of the VolumeAttributesClass", "in": "path", "name": "name", "required": true, "type": "string", "uniqueItems": true }, - { - "$ref": "#/parameters/namespace-vgWSWtn3" - }, { "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" } - ] - }, - "/apis/storage.k8s.io/v1/watch/storageclasses": { - "get": { + ], + "patch": { "consumes": [ - "*/*" + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml", + "application/apply-patch+cbor" + ], + "description": "partially update the specified VolumeAttributesClass", + "operationId": "patchStorageV1VolumeAttributesClass", + "parameters": [ + { + "$ref": "#/parameters/body-78PwaGsr" + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-7c6nTn1T" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/force-tOGGb0Yi" + } ], - "description": "watch individual changes to a list of StorageClass. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1StorageClassList", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, "401": { @@ -92590,70 +93066,63 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watchlist", + "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "VolumeAttributesClass", "version": "v1" } }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] - }, - "/apis/storage.k8s.io/v1/watch/storageclasses/{name}": { - "get": { + "put": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind StorageClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1StorageClass", + "description": "replace the specified VolumeAttributesClass", + "operationId": "replaceStorageV1VolumeAttributesClass", + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/fieldManager-Qy4HdaTW" + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "type": "string", + "uniqueItems": true + } + ], "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch", - "application/cbor-seq" + "application/cbor" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.k8s.api.storage.v1.VolumeAttributesClass" } }, "401": { @@ -92666,64 +93135,21 @@ "tags": [ "storage_v1" ], - "x-kubernetes-action": "watch", + "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "StorageClass", + "kind": "VolumeAttributesClass", "version": "v1" } - }, - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "description": "name of the StorageClass", - "in": "path", - "name": "name", - "required": true, - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/pretty-tJGM1-ng" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } - ] + } }, - "/apis/storage.k8s.io/v1/watch/volumeattachments": { + "/apis/storage.k8s.io/v1/watch/csidrivers": { "get": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of VolumeAttachment. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1VolumeAttachmentList", + "description": "watch individual changes to a list of CSIDriver. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1CSIDriverList", "produces": [ "application/json", "application/yaml", @@ -92753,7 +93179,7 @@ "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "CSIDriver", "version": "v1" } }, @@ -92793,13 +93219,13 @@ } ] }, - "/apis/storage.k8s.io/v1/watch/volumeattachments/{name}": { + "/apis/storage.k8s.io/v1/watch/csidrivers/{name}": { "get": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind VolumeAttachment. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1VolumeAttachment", + "description": "watch changes to an object of kind CSIDriver. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStorageV1CSIDriver", "produces": [ "application/json", "application/yaml", @@ -92829,7 +93255,7 @@ "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttachment", + "kind": "CSIDriver", "version": "v1" } }, @@ -92850,7 +93276,7 @@ "$ref": "#/parameters/limit-1NfNmdNH" }, { - "description": "name of the VolumeAttachment", + "description": "name of the CSIDriver", "in": "path", "name": "name", "required": true, @@ -92877,13 +93303,13 @@ } ] }, - "/apis/storage.k8s.io/v1/watch/volumeattributesclasses": { + "/apis/storage.k8s.io/v1/watch/csinodes": { "get": { "consumes": [ "*/*" ], - "description": "watch individual changes to a list of VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1VolumeAttributesClassList", + "description": "watch individual changes to a list of CSINode. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1CSINodeList", "produces": [ "application/json", "application/yaml", @@ -92913,7 +93339,7 @@ "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "CSINode", "version": "v1" } }, @@ -92953,13 +93379,13 @@ } ] }, - "/apis/storage.k8s.io/v1/watch/volumeattributesclasses/{name}": { + "/apis/storage.k8s.io/v1/watch/csinodes/{name}": { "get": { "consumes": [ "*/*" ], - "description": "watch changes to an object of kind VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1VolumeAttributesClass", + "description": "watch changes to an object of kind CSINode. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStorageV1CSINode", "produces": [ "application/json", "application/yaml", @@ -92989,7 +93415,7 @@ "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", + "kind": "CSINode", "version": "v1" } }, @@ -93010,7 +93436,7 @@ "$ref": "#/parameters/limit-1NfNmdNH" }, { - "description": "name of the VolumeAttributesClass", + "description": "name of the CSINode", "in": "path", "name": "name", "required": true, @@ -93037,107 +93463,27 @@ } ] }, - "/apis/storage.k8s.io/v1alpha1/": { + "/apis/storage.k8s.io/v1/watch/csistoragecapacities": { "get": { - "consumes": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" - ], - "description": "get available resources", - "operationId": "getStorageV1alpha1APIResources", - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/cbor" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "401": { - "description": "Unauthorized" - } - }, - "schemes": [ - "https" - ], - "tags": [ - "storage_v1alpha1" - ] - } - }, - "/apis/storage.k8s.io/v1alpha1/volumeattributesclasses": { - "delete": { "consumes": [ "*/*" ], - "description": "delete collection of VolumeAttributesClass", - "operationId": "deleteStorageV1alpha1CollectionVolumeAttributesClass", - "parameters": [ - { - "$ref": "#/parameters/body-2Y1dVQaQ" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" - }, - { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - } - ], + "description": "watch individual changes to a list of CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1CSIStorageCapacityListForAllNamespaces", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93148,53 +93494,58 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "deletecollection", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "CSIStorageCapacity", + "version": "v1" } }, - "get": { - "consumes": [ - "*/*" - ], - "description": "list or watch objects of kind VolumeAttributesClass", - "operationId": "listStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" - }, - { - "$ref": "#/parameters/continue-QfD61s0i" - }, - { - "$ref": "#/parameters/fieldSelector-xIcQKXFG" - }, - { - "$ref": "#/parameters/labelSelector-5Zw57w4C" - }, - { - "$ref": "#/parameters/limit-1NfNmdNH" - }, - { - "$ref": "#/parameters/resourceVersion-5WAnf1kx" - }, - { - "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" - }, - { - "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" - }, - { - "$ref": "#/parameters/timeoutSeconds-yvYezaOC" - }, - { - "$ref": "#/parameters/watch-XNNPZGbK" - } + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/storage.k8s.io/v1/watch/namespaces/{namespace}/csistoragecapacities": { + "get": { + "consumes": [ + "*/*" ], + "description": "watch individual changes to a list of CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1NamespacedCSIStorageCapacityList", "produces": [ "application/json", "application/yaml", @@ -93208,7 +93559,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93219,76 +93570,75 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "list", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "CSIStorageCapacity", + "version": "v1" } }, "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, { "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } - ], - "post": { + ] + }, + "/apis/storage.k8s.io/v1/watch/namespaces/{namespace}/csistoragecapacities/{name}": { + "get": { "consumes": [ "*/*" ], - "description": "create a VolumeAttributesClass", - "operationId": "createStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-Qy4HdaTW" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - } - ], + "description": "watch changes to an object of kind CSIStorageCapacity. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStorageV1NamespacedCSIStorageCapacity", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93299,64 +93649,83 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "post", + "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "CSIStorageCapacity", + "version": "v1" } - } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the CSIStorageCapacity", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/namespace-vgWSWtn3" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] }, - "/apis/storage.k8s.io/v1alpha1/volumeattributesclasses/{name}": { - "delete": { + "/apis/storage.k8s.io/v1/watch/storageclasses": { + "get": { "consumes": [ "*/*" ], - "description": "delete a VolumeAttributesClass", - "operationId": "deleteStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "$ref": "#/parameters/body-2Y1dVQaQ" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/gracePeriodSeconds--K5HaBOS" - }, - { - "$ref": "#/parameters/ignoreStoreReadErrorWithClusterBreakingPotential-QbNkfIqj" - }, - { - "$ref": "#/parameters/orphanDependents-uRB25kX5" - }, - { - "$ref": "#/parameters/propagationPolicy-6jk3prlO" - } - ], + "description": "watch individual changes to a list of StorageClass. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1StorageClassList", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "202": { - "description": "Accepted", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93367,32 +93736,72 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "delete", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "StorageClass", + "version": "v1" } }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/storage.k8s.io/v1/watch/storageclasses/{name}": { "get": { "consumes": [ "*/*" ], - "description": "read the specified VolumeAttributesClass", - "operationId": "readStorageV1alpha1VolumeAttributesClass", + "description": "watch changes to an object of kind StorageClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStorageV1StorageClass", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93403,18 +93812,33 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "get", + "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "StorageClass", + "version": "v1" } }, "parameters": [ { - "description": "name of the VolumeAttributesClass", + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the StorageClass", "in": "path", "name": "name", "required": true, @@ -93423,60 +93847,45 @@ }, { "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" } - ], - "patch": { + ] + }, + "/apis/storage.k8s.io/v1/watch/volumeattachments": { + "get": { "consumes": [ - "application/json-patch+json", - "application/merge-patch+json", - "application/strategic-merge-patch+json", - "application/apply-patch+yaml", - "application/apply-patch+cbor" - ], - "description": "partially update the specified VolumeAttributesClass", - "operationId": "patchStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "$ref": "#/parameters/body-78PwaGsr" - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-7c6nTn1T" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/force-tOGGb0Yi" - } + "*/*" ], + "description": "watch individual changes to a list of VolumeAttachment. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStorageV1VolumeAttachmentList", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93487,65 +93896,72 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "patch", + "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "VolumeAttachment", + "version": "v1" } }, - "put": { + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] + }, + "/apis/storage.k8s.io/v1/watch/volumeattachments/{name}": { + "get": { "consumes": [ "*/*" ], - "description": "replace the specified VolumeAttributesClass", - "operationId": "replaceStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "type": "string", - "uniqueItems": true - }, - { - "$ref": "#/parameters/fieldManager-Qy4HdaTW" - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "type": "string", - "uniqueItems": true - } - ], + "description": "watch changes to an object of kind VolumeAttachment. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStorageV1VolumeAttachment", "produces": [ "application/json", "application/yaml", "application/vnd.kubernetes.protobuf", - "application/cbor" + "application/cbor", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch", + "application/cbor-seq" ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" } }, "401": { @@ -93556,23 +93972,66 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], - "x-kubernetes-action": "put", + "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "kind": "VolumeAttachment", + "version": "v1" } - } + }, + "parameters": [ + { + "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" + }, + { + "$ref": "#/parameters/continue-QfD61s0i" + }, + { + "$ref": "#/parameters/fieldSelector-xIcQKXFG" + }, + { + "$ref": "#/parameters/labelSelector-5Zw57w4C" + }, + { + "$ref": "#/parameters/limit-1NfNmdNH" + }, + { + "description": "name of the VolumeAttachment", + "in": "path", + "name": "name", + "required": true, + "type": "string", + "uniqueItems": true + }, + { + "$ref": "#/parameters/pretty-tJGM1-ng" + }, + { + "$ref": "#/parameters/resourceVersion-5WAnf1kx" + }, + { + "$ref": "#/parameters/resourceVersionMatch-t8XhRHeC" + }, + { + "$ref": "#/parameters/sendInitialEvents-rLXlEK_k" + }, + { + "$ref": "#/parameters/timeoutSeconds-yvYezaOC" + }, + { + "$ref": "#/parameters/watch-XNNPZGbK" + } + ] }, - "/apis/storage.k8s.io/v1alpha1/watch/volumeattributesclasses": { + "/apis/storage.k8s.io/v1/watch/volumeattributesclasses": { "get": { "consumes": [ "*/*" ], "description": "watch individual changes to a list of VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1alpha1VolumeAttributesClassList", + "operationId": "watchStorageV1VolumeAttributesClassList", "produces": [ "application/json", "application/yaml", @@ -93597,13 +94056,13 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "version": "v1" } }, "parameters": [ @@ -93642,13 +94101,13 @@ } ] }, - "/apis/storage.k8s.io/v1alpha1/watch/volumeattributesclasses/{name}": { + "/apis/storage.k8s.io/v1/watch/volumeattributesclasses/{name}": { "get": { "consumes": [ "*/*" ], "description": "watch changes to an object of kind VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1alpha1VolumeAttributesClass", + "operationId": "watchStorageV1VolumeAttributesClass", "produces": [ "application/json", "application/yaml", @@ -93673,13 +94132,13 @@ "https" ], "tags": [ - "storage_v1alpha1" + "storage_v1" ], "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storage.k8s.io", "kind": "VolumeAttributesClass", - "version": "v1alpha1" + "version": "v1" } }, "parameters": [ @@ -94448,7 +94907,7 @@ ] } }, - "/apis/storagemigration.k8s.io/v1alpha1/": { + "/apis/storagemigration.k8s.io/v1beta1/": { "get": { "consumes": [ "application/json", @@ -94457,7 +94916,7 @@ "application/cbor" ], "description": "get available resources", - "operationId": "getStoragemigrationV1alpha1APIResources", + "operationId": "getStoragemigrationV1beta1APIResources", "produces": [ "application/json", "application/yaml", @@ -94479,17 +94938,17 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ] } }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations": { + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations": { "delete": { "consumes": [ "*/*" ], "description": "delete collection of StorageVersionMigration", - "operationId": "deleteStoragemigrationV1alpha1CollectionStorageVersionMigration", + "operationId": "deleteStoragemigrationV1beta1CollectionStorageVersionMigration", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -94559,13 +95018,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "deletecollection", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "get": { @@ -94573,7 +95032,7 @@ "*/*" ], "description": "list or watch objects of kind StorageVersionMigration", - "operationId": "listStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "listStoragemigrationV1beta1StorageVersionMigration", "parameters": [ { "$ref": "#/parameters/allowWatchBookmarks-HC2hJt-J" @@ -94619,7 +95078,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" } }, "401": { @@ -94630,13 +95089,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ @@ -94649,14 +95108,14 @@ "*/*" ], "description": "create a StorageVersionMigration", - "operationId": "createStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "createStoragemigrationV1beta1StorageVersionMigration", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, { @@ -94687,19 +95146,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -94710,23 +95169,23 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } } }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations/{name}": { + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations/{name}": { "delete": { "consumes": [ "*/*" ], "description": "delete a StorageVersionMigration", - "operationId": "deleteStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "deleteStoragemigrationV1beta1StorageVersionMigration", "parameters": [ { "$ref": "#/parameters/body-2Y1dVQaQ" @@ -94778,13 +95237,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "delete", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "get": { @@ -94792,7 +95251,7 @@ "*/*" ], "description": "read the specified StorageVersionMigration", - "operationId": "readStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "readStoragemigrationV1beta1StorageVersionMigration", "produces": [ "application/json", "application/yaml", @@ -94803,7 +95262,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -94814,13 +95273,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ @@ -94845,7 +95304,7 @@ "application/apply-patch+cbor" ], "description": "partially update the specified StorageVersionMigration", - "operationId": "patchStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "patchStoragemigrationV1beta1StorageVersionMigration", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -94881,13 +95340,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -94898,13 +95357,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "put": { @@ -94912,14 +95371,14 @@ "*/*" ], "description": "replace the specified StorageVersionMigration", - "operationId": "replaceStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "replaceStoragemigrationV1beta1StorageVersionMigration", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, { @@ -94950,13 +95409,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -94967,23 +95426,23 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } } }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations/{name}/status": { + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations/{name}/status": { "get": { "consumes": [ "*/*" ], "description": "read status of the specified StorageVersionMigration", - "operationId": "readStoragemigrationV1alpha1StorageVersionMigrationStatus", + "operationId": "readStoragemigrationV1beta1StorageVersionMigrationStatus", "produces": [ "application/json", "application/yaml", @@ -94994,7 +95453,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -95005,13 +95464,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "get", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ @@ -95036,7 +95495,7 @@ "application/apply-patch+cbor" ], "description": "partially update status of the specified StorageVersionMigration", - "operationId": "patchStoragemigrationV1alpha1StorageVersionMigrationStatus", + "operationId": "patchStoragemigrationV1beta1StorageVersionMigrationStatus", "parameters": [ { "$ref": "#/parameters/body-78PwaGsr" @@ -95072,13 +95531,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -95089,13 +95548,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "patch", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "put": { @@ -95103,14 +95562,14 @@ "*/*" ], "description": "replace status of the specified StorageVersionMigration", - "operationId": "replaceStoragemigrationV1alpha1StorageVersionMigrationStatus", + "operationId": "replaceStoragemigrationV1beta1StorageVersionMigrationStatus", "parameters": [ { "in": "body", "name": "body", "required": true, "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, { @@ -95141,13 +95600,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" + "$ref": "#/definitions/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" } }, "401": { @@ -95158,23 +95617,23 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "put", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } } }, - "/apis/storagemigration.k8s.io/v1alpha1/watch/storageversionmigrations": { + "/apis/storagemigration.k8s.io/v1beta1/watch/storageversionmigrations": { "get": { "consumes": [ "*/*" ], "description": "watch individual changes to a list of StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStoragemigrationV1alpha1StorageVersionMigrationList", + "operationId": "watchStoragemigrationV1beta1StorageVersionMigrationList", "produces": [ "application/json", "application/yaml", @@ -95199,13 +95658,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ @@ -95244,13 +95703,13 @@ } ] }, - "/apis/storagemigration.k8s.io/v1alpha1/watch/storageversionmigrations/{name}": { + "/apis/storagemigration.k8s.io/v1beta1/watch/storageversionmigrations/{name}": { "get": { "consumes": [ "*/*" ], "description": "watch changes to an object of kind StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStoragemigrationV1alpha1StorageVersionMigration", + "operationId": "watchStoragemigrationV1beta1StorageVersionMigration", "produces": [ "application/json", "application/yaml", @@ -95275,13 +95734,13 @@ "https" ], "tags": [ - "storagemigration_v1alpha1" + "storagemigration_v1beta1" ], "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "storagemigration.k8s.io", "kind": "StorageVersionMigration", - "version": "v1alpha1" + "version": "v1beta1" } }, "parameters": [ diff --git a/api/openapi-spec/v3/api__v1_openapi.json b/api/openapi-spec/v3/api__v1_openapi.json index e97c10daeafab..8125c8ce3fc03 100644 --- a/api/openapi-spec/v3/api__v1_openapi.json +++ b/api/openapi-spec/v3/api__v1_openapi.json @@ -1230,7 +1230,7 @@ "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" }, "resizePolicy": { - "description": "Resources resize policy for the container.", + "description": "Resources resize policy for the container. This field cannot be set on ephemeral containers.", "items": { "allOf": [ { @@ -4222,6 +4222,15 @@ "default": {}, "description": "Endpoints of daemons running on the Node." }, + "declaredFeatures": { + "description": "DeclaredFeatures represents the features related to feature gates that are declared by the node.", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, "features": { "allOf": [ { @@ -4655,7 +4664,7 @@ } ], "default": {}, - "description": "resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + "description": "resources represents the minimum resources the volume should have. Users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" }, "selector": { "allOf": [ @@ -4701,7 +4710,7 @@ "default": "", "type": "string" }, - "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object", "x-kubernetes-map-type": "granular" }, @@ -4709,7 +4718,7 @@ "additionalProperties": { "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.api.resource.Quantity" }, - "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object" }, "capacity": { @@ -5009,7 +5018,7 @@ "$ref": "#/components/schemas/io.k8s.api.core.v1.VolumeNodeAffinity" } ], - "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume." + "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume. This field is mutable if MutablePVNodeAffinity feature gate is enabled." }, "persistentVolumeReclaimPolicy": { "description": "persistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", @@ -5326,6 +5335,14 @@ "signerName": { "description": "Kubelet's generated CSRs will be addressed to this signer.", "type": "string" + }, + "userAnnotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "userAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nThese values are copied verbatim into the `spec.unverifiedUserAnnotations` field of the PodCertificateRequest objects that Kubelet creates.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" } }, "required": [ @@ -5358,7 +5375,7 @@ "type": "string" }, "observedGeneration": { - "description": "If set, this represents the .metadata.generation that the pod condition was set based upon. This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field.", + "description": "If set, this represents the .metadata.generation that the pod condition was set based upon. The PodObservedGenerationTracking feature gate must be enabled to use this field.", "format": "int64", "type": "integer" }, @@ -5900,7 +5917,7 @@ "x-kubernetes-list-type": "atomic" }, "resourceClaims": { - "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is a stable field but requires that the DynamicResourceAllocation feature gate is enabled.\n\nThis field is immutable.", "items": { "allOf": [ { @@ -6037,6 +6054,14 @@ "x-kubernetes-list-type": "map", "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "workloadRef": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.WorkloadReference" + } + ], + "description": "WorkloadRef provides a reference to the Workload object that this Pod belongs to. This field is used by the scheduler to identify the PodGroup and apply the correct group scheduling policies. The Workload object referenced by this field may not exist at the time the Pod is created. This field is immutable, but a Workload object with the same name may be recreated with different policies. Doing this during pod scheduling may result in the placement not conforming to the expected policies." } }, "required": [ @@ -6047,6 +6072,13 @@ "io.k8s.api.core.v1.PodStatus": { "description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", "properties": { + "allocatedResources": { + "additionalProperties": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "description": "AllocatedResources is the total requests allocated for this pod by the node. If pod-level requests are not set, this will be the total requests aggregated across containers in the pod.", + "type": "object" + }, "conditions": { "description": "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", "items": { @@ -6140,7 +6172,7 @@ "type": "string" }, "observedGeneration": { - "description": "If set, this represents the .metadata.generation that the pod status was set based upon. This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field.", + "description": "If set, this represents the .metadata.generation that the pod status was set based upon. The PodObservedGenerationTracking feature gate must be enabled to use this field.", "format": "int64", "type": "integer" }, @@ -6200,6 +6232,14 @@ "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" }, + "resources": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.ResourceRequirements" + } + ], + "description": "Resources represents the compute resource requests and limits that have been applied at the pod level if pod-level requests or limits are set in PodSpec.Resources" + }, "startTime": { "allOf": [ { @@ -8227,7 +8267,7 @@ "type": "string" }, "operator": { - "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "description": "Operator represents a key's relationship to the value. Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).", "type": "string" }, "tolerationSeconds": { @@ -8855,6 +8895,30 @@ }, "type": "object" }, + "io.k8s.api.core.v1.WorkloadReference": { + "description": "WorkloadReference identifies the Workload object and PodGroup membership that a Pod belongs to. The scheduler uses this information to apply workload-aware scheduling semantics.", + "properties": { + "name": { + "default": "", + "description": "Name defines the name of the Workload object this Pod belongs to. Workload must be in the same namespace as the Pod. If it doesn't match any existing Workload, the Pod will remain unschedulable until a Workload object is created and observed by the kube-scheduler. It must be a DNS subdomain.", + "type": "string" + }, + "podGroup": { + "default": "", + "description": "PodGroup is the name of the PodGroup within the Workload that this Pod belongs to. If it doesn't match any existing PodGroup within the Workload, the Pod will remain unschedulable until the Workload object is recreated and observed by the kube-scheduler. It must be a DNS label.", + "type": "string" + }, + "podGroupReplicaKey": { + "description": "PodGroupReplicaKey specifies the replica key of the PodGroup to which this Pod belongs. It is used to distinguish pods belonging to different replicas of the same pod group. The pod group policy is applied separately to each replica. When set, it must be a DNS label.", + "type": "string" + } + }, + "required": [ + "name", + "podGroup" + ], + "type": "object" + }, "io.k8s.api.policy.v1.Eviction": { "description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods//evictions.", "properties": { @@ -9420,7 +9484,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -9745,8 +9809,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -10176,7 +10239,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1_openapi.json index 8052dfe74be3e..cb41191363958 100644 --- a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1_openapi.json @@ -1615,7 +1615,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1935,8 +1935,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2366,7 +2365,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json index 58ce51a89a79f..c5ececfdf252e 100644 --- a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json +++ b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json @@ -979,7 +979,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1299,8 +1299,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1730,7 +1729,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1beta1_openapi.json index fd68d7687caf2..9f7492da45b6b 100644 --- a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1beta1_openapi.json @@ -981,7 +981,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1301,8 +1301,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1732,7 +1731,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json index 515e9554b1220..8f7af3a53f336 100644 --- a/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json @@ -128,6 +128,11 @@ "description": "message is a human-readable message indicating details about last transition.", "type": "string" }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, "reason": { "description": "reason is a unique, one-word, CamelCase reason for the condition's last transition.", "type": "string" @@ -324,6 +329,11 @@ ], "x-kubernetes-list-type": "map" }, + "observedGeneration": { + "description": "The generation observed by the CRD controller.", + "format": "int64", + "type": "integer" + }, "storedVersions": { "description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.", "items": { @@ -1338,7 +1348,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1601,8 +1611,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2032,7 +2041,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__apiregistration.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__apiregistration.k8s.io__v1_openapi.json index 93a04cde7c05b..2eb04c986d1ee 100644 --- a/api/openapi-spec/v3/apis__apiregistration.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__apiregistration.k8s.io__v1_openapi.json @@ -471,7 +471,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -734,8 +734,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1165,7 +1164,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__apps__v1_openapi.json b/api/openapi-spec/v3/apis__apps__v1_openapi.json index 4224213dd3981..15df0b3735ed8 100644 --- a/api/openapi-spec/v3/apis__apps__v1_openapi.json +++ b/api/openapi-spec/v3/apis__apps__v1_openapi.json @@ -610,7 +610,7 @@ "type": "integer" }, "terminatingReplicas": { - "description": "Total number of terminating pods targeted by this deployment. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field.", + "description": "Total number of terminating pods targeted by this deployment. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default).", "format": "int32", "type": "integer" }, @@ -858,7 +858,7 @@ "type": "integer" }, "terminatingReplicas": { - "description": "The number of terminating pods for this replica set. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field.", + "description": "The number of terminating pods for this replica set. Terminating pods have a non-null .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase.\n\nThis is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default).", "format": "int32", "type": "integer" } @@ -921,7 +921,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.util.intstr.IntOrString" } ], - "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is alpha-level and is only honored by servers that enable the MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable." + "description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is beta-level and is enabled by default. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable. This setting might not be effective for the OrderedReady podManagementPolicy. That policy ensures pods are created and become ready one at a time." }, "partition": { "description": "Partition indicates the ordinal at which the StatefulSet should be partitioned for updates. During a rolling update, all pods from ordinal Replicas-1 to Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. This is helpful in being able to do a canary based deployment. The default value is 0.", @@ -1868,7 +1868,7 @@ "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" }, "resizePolicy": { - "description": "Resources resize policy for the container.", + "description": "Resources resize policy for the container. This field cannot be set on ephemeral containers.", "items": { "allOf": [ { @@ -3342,7 +3342,7 @@ } ], "default": {}, - "description": "resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + "description": "resources represents the minimum resources the volume should have. Users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" }, "selector": { "allOf": [ @@ -3388,7 +3388,7 @@ "default": "", "type": "string" }, - "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nClaimResourceStatus can be in any of following states:\n\t- ControllerResizeInProgress:\n\t\tState set when resize controller starts resizing the volume in control-plane.\n\t- ControllerResizeFailed:\n\t\tState set when resize has failed in resize controller with a terminal error.\n\t- NodeResizePending:\n\t\tState set when resize controller has finished resizing the volume but further resizing of\n\t\tvolume is needed on the node.\n\t- NodeResizeInProgress:\n\t\tState set when kubelet starts resizing the volume.\n\t- NodeResizeFailed:\n\t\tState set when resizing has failed in kubelet with a terminal error. Transient errors don't set\n\t\tNodeResizeFailed.\nFor example: if expanding a PVC for more capacity - this field can be one of the following states:\n\t- pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"ControllerResizeFailed\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizePending\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeInProgress\"\n - pvc.status.allocatedResourceStatus['storage'] = \"NodeResizeFailed\"\nWhen this field is not set, it means that no resize operation is in progress for the given PVC.\n\nA controller that receives PVC update with previously unknown resourceName or ClaimResourceStatus should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object", "x-kubernetes-map-type": "granular" }, @@ -3396,7 +3396,7 @@ "additionalProperties": { "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.api.resource.Quantity" }, - "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.\n\nThis is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.", + "description": "allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax. Valid values are either:\n\t* Un-prefixed keys:\n\t\t- storage - the capacity of the volume.\n\t* Custom resources must use implementation-defined prefixed names such as \"example.com/my-custom-resource\"\nApart from above values - keys that are unprefixed or have kubernetes.io prefix are considered reserved and hence may not be used.\n\nCapacity reported here may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity.\n\nA controller that receives PVC update with previously unknown resourceName should ignore the update for the purpose it was designed. For example - a controller that only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid resources associated with PVC.", "type": "object" }, "capacity": { @@ -3654,6 +3654,14 @@ "signerName": { "description": "Kubelet's generated CSRs will be addressed to this signer.", "type": "string" + }, + "userAnnotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "userAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nThese values are copied verbatim into the `spec.unverifiedUserAnnotations` field of the PodCertificateRequest objects that Kubelet creates.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" } }, "required": [ @@ -4074,7 +4082,7 @@ "x-kubernetes-list-type": "atomic" }, "resourceClaims": { - "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is a stable field but requires that the DynamicResourceAllocation feature gate is enabled.\n\nThis field is immutable.", "items": { "allOf": [ { @@ -4211,6 +4219,14 @@ "x-kubernetes-list-type": "map", "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "workloadRef": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.WorkloadReference" + } + ], + "description": "WorkloadRef provides a reference to the Workload object that this Pod belongs to. This field is used by the scheduler to identify the PodGroup and apply the correct group scheduling policies. The Workload object referenced by this field may not exist at the time the Pod is created. This field is immutable, but a Workload object with the same name may be recreated with different policies. Doing this during pod scheduling may result in the placement not conforming to the expected policies." } }, "required": [ @@ -4956,7 +4972,7 @@ "type": "string" }, "operator": { - "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "description": "Operator represents a key's relationship to the value. Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).", "type": "string" }, "tolerationSeconds": { @@ -5542,6 +5558,30 @@ }, "type": "object" }, + "io.k8s.api.core.v1.WorkloadReference": { + "description": "WorkloadReference identifies the Workload object and PodGroup membership that a Pod belongs to. The scheduler uses this information to apply workload-aware scheduling semantics.", + "properties": { + "name": { + "default": "", + "description": "Name defines the name of the Workload object this Pod belongs to. Workload must be in the same namespace as the Pod. If it doesn't match any existing Workload, the Pod will remain unschedulable until a Workload object is created and observed by the kube-scheduler. It must be a DNS subdomain.", + "type": "string" + }, + "podGroup": { + "default": "", + "description": "PodGroup is the name of the PodGroup within the Workload that this Pod belongs to. If it doesn't match any existing PodGroup within the Workload, the Pod will remain unschedulable until the Workload object is recreated and observed by the kube-scheduler. It must be a DNS label.", + "type": "string" + }, + "podGroupReplicaKey": { + "description": "PodGroupReplicaKey specifies the replica key of the PodGroup to which this Pod belongs. It is used to distinguish pods belonging to different replicas of the same pod group. The pod group policy is applied separately to each replica. When set, it must be a DNS label.", + "type": "string" + } + }, + "required": [ + "name", + "podGroup" + ], + "type": "object" + }, "io.k8s.apimachinery.pkg.api.resource.Quantity": { "description": "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.", "oneOf": [ @@ -6023,7 +6063,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -6343,8 +6383,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -6774,7 +6813,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__autoscaling__v1_openapi.json b/api/openapi-spec/v3/apis__autoscaling__v1_openapi.json index e89070b446384..a6a895b4bd9f2 100644 --- a/api/openapi-spec/v3/apis__autoscaling__v1_openapi.json +++ b/api/openapi-spec/v3/apis__autoscaling__v1_openapi.json @@ -664,7 +664,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -927,8 +927,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1358,7 +1357,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__autoscaling__v2_openapi.json b/api/openapi-spec/v3/apis__autoscaling__v2_openapi.json index 011a562d396f0..da9e7ef04f965 100644 --- a/api/openapi-spec/v3/apis__autoscaling__v2_openapi.json +++ b/api/openapi-spec/v3/apis__autoscaling__v2_openapi.json @@ -170,7 +170,7 @@ "type": "object" }, "io.k8s.api.autoscaling.v2.HPAScalingRules": { - "description": "HPAScalingRules configures the scaling behavior for one direction via scaling Policy Rules and a configurable metric tolerance.\n\nScaling Policy Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.\n\nThe tolerance is applied to the metric values and prevents scaling too eagerly for small metric variations. (Note that setting a tolerance requires enabling the alpha HPAConfigurableTolerance feature gate.)", + "description": "HPAScalingRules configures the scaling behavior for one direction via scaling Policy Rules and a configurable metric tolerance.\n\nScaling Policy Rules are applied after calculating DesiredReplicas from metrics for the HPA. They can limit the scaling velocity by specifying scaling policies. They can prevent flapping by specifying the stabilization window, so that the number of replicas is not set instantly, instead, the safest value from the stabilization window is chosen.\n\nThe tolerance is applied to the metric values and prevents scaling too eagerly for small metric variations. (Note that setting a tolerance requires the beta HPAConfigurableTolerance feature gate to be enabled.)", "properties": { "policies": { "description": "policies is a list of potential scaling polices which can be used during scaling. If not set, use the default values: - For scale up: allow doubling the number of pods, or an absolute change of 4 pods in a 15s window. - For scale down: allow all pods to be removed in a 15s window.", @@ -200,7 +200,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.api.resource.Quantity" } ], - "description": "tolerance is the tolerance on the ratio between the current and desired metric value under which no updates are made to the desired number of replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not set, the default cluster-wide tolerance is applied (by default 10%).\n\nFor example, if autoscaling is configured with a memory consumption target of 100Mi, and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be triggered when the actual consumption falls below 95Mi or exceeds 101Mi.\n\nThis is an alpha field and requires enabling the HPAConfigurableTolerance feature gate." + "description": "tolerance is the tolerance on the ratio between the current and desired metric value under which no updates are made to the desired number of replicas (e.g. 0.01 for 1%). Must be greater than or equal to zero. If not set, the default cluster-wide tolerance is applied (by default 10%).\n\nFor example, if autoscaling is configured with a memory consumption target of 100Mi, and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be triggered when the actual consumption falls below 95Mi or exceeds 101Mi.\n\nThis is an beta field and requires the HPAConfigurableTolerance feature gate to be enabled." } }, "type": "object" @@ -1324,7 +1324,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1644,8 +1644,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2075,7 +2074,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__batch__v1_openapi.json b/api/openapi-spec/v3/apis__batch__v1_openapi.json index fb22cbf0f978e..2df1e47230b57 100644 --- a/api/openapi-spec/v3/apis__batch__v1_openapi.json +++ b/api/openapi-spec/v3/apis__batch__v1_openapi.json @@ -345,7 +345,7 @@ "type": "integer" }, "managedBy": { - "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `kubernetes.io/job-controller`, but skips reconciling Jobs with a custom value for this field. The value must be a valid domain-prefixed path (e.g. acme.io/foo) - all characters before the first \"/\" must be a valid subdomain as defined by RFC 1123. All characters trailing the first \"/\" must be valid HTTP Path characters as defined by RFC 3986. The value cannot exceed 63 characters. This field is immutable.\n\nThis field is beta-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (enabled by default).", + "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `kubernetes.io/job-controller`, but skips reconciling Jobs with a custom value for this field. The value must be a valid domain-prefixed path (e.g. acme.io/foo) - all characters before the first \"/\" must be a valid subdomain as defined by RFC 1123. All characters trailing the first \"/\" must be valid HTTP Path characters as defined by RFC 3986. The value cannot exceed 63 characters. This field is immutable.", "type": "string" }, "manualSelector": { @@ -582,8 +582,7 @@ } }, "required": [ - "type", - "status" + "type" ], "type": "object" }, @@ -1207,7 +1206,7 @@ "description": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes" }, "resizePolicy": { - "description": "Resources resize policy for the container.", + "description": "Resources resize policy for the container. This field cannot be set on ephemeral containers.", "items": { "allOf": [ { @@ -2606,7 +2605,7 @@ } ], "default": {}, - "description": "resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" + "description": "resources represents the minimum resources the volume should have. Users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources" }, "selector": { "allOf": [ @@ -2846,6 +2845,14 @@ "signerName": { "description": "Kubelet's generated CSRs will be addressed to this signer.", "type": "string" + }, + "userAnnotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "userAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nThese values are copied verbatim into the `spec.unverifiedUserAnnotations` field of the PodCertificateRequest objects that Kubelet creates.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" } }, "required": [ @@ -3266,7 +3273,7 @@ "x-kubernetes-list-type": "atomic" }, "resourceClaims": { - "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is an alpha field and requires enabling the DynamicResourceAllocation feature gate.\n\nThis field is immutable.", + "description": "ResourceClaims defines which ResourceClaims must be allocated and reserved before the Pod is allowed to start. The resources will be made available to those containers which consume them by name.\n\nThis is a stable field but requires that the DynamicResourceAllocation feature gate is enabled.\n\nThis field is immutable.", "items": { "allOf": [ { @@ -3403,6 +3410,14 @@ "x-kubernetes-list-type": "map", "x-kubernetes-patch-merge-key": "name", "x-kubernetes-patch-strategy": "merge,retainKeys" + }, + "workloadRef": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.core.v1.WorkloadReference" + } + ], + "description": "WorkloadRef provides a reference to the Workload object that this Pod belongs to. This field is used by the scheduler to identify the PodGroup and apply the correct group scheduling policies. The Workload object referenced by this field may not exist at the time the Pod is created. This field is immutable, but a Workload object with the same name may be recreated with different policies. Doing this during pod scheduling may result in the placement not conforming to the expected policies." } }, "required": [ @@ -4148,7 +4163,7 @@ "type": "string" }, "operator": { - "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "description": "Operator represents a key's relationship to the value. Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).", "type": "string" }, "tolerationSeconds": { @@ -4734,6 +4749,30 @@ }, "type": "object" }, + "io.k8s.api.core.v1.WorkloadReference": { + "description": "WorkloadReference identifies the Workload object and PodGroup membership that a Pod belongs to. The scheduler uses this information to apply workload-aware scheduling semantics.", + "properties": { + "name": { + "default": "", + "description": "Name defines the name of the Workload object this Pod belongs to. Workload must be in the same namespace as the Pod. If it doesn't match any existing Workload, the Pod will remain unschedulable until a Workload object is created and observed by the kube-scheduler. It must be a DNS subdomain.", + "type": "string" + }, + "podGroup": { + "default": "", + "description": "PodGroup is the name of the PodGroup within the Workload that this Pod belongs to. If it doesn't match any existing PodGroup within the Workload, the Pod will remain unschedulable until the Workload object is recreated and observed by the kube-scheduler. It must be a DNS label.", + "type": "string" + }, + "podGroupReplicaKey": { + "description": "PodGroupReplicaKey specifies the replica key of the PodGroup to which this Pod belongs. It is used to distinguish pods belonging to different replicas of the same pod group. The pod group policy is applied separately to each replica. When set, it must be a DNS label.", + "type": "string" + } + }, + "required": [ + "name", + "podGroup" + ], + "type": "object" + }, "io.k8s.apimachinery.pkg.api.resource.Quantity": { "description": "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.", "oneOf": [ @@ -5215,7 +5254,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -5535,8 +5574,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -5966,7 +6004,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__certificates.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__certificates.k8s.io__v1_openapi.json index 41ca88bd80dd4..980da428dc7a3 100644 --- a/api/openapi-spec/v3/apis__certificates.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__certificates.k8s.io__v1_openapi.json @@ -170,8 +170,7 @@ "request": { "description": "request contains an x509 certificate signing request encoded in a \"CERTIFICATE REQUEST\" PEM block. When serialized as JSON or YAML, the data is additionally base64-encoded.", "format": "byte", - "type": "string", - "x-kubernetes-list-type": "atomic" + "type": "string" }, "signerName": { "default": "", @@ -208,8 +207,7 @@ "certificate": { "description": "certificate is populated with an issued certificate by the signer after an Approved condition is present. This field is set via the /status subresource. Once populated, this field is immutable.\n\nIf the certificate signing request is denied, a condition of type \"Denied\" is added and this field remains empty. If the signer cannot issue the certificate, a condition of type \"Failed\" is added and this field remains empty.\n\nValidation requirements:\n 1. certificate must contain one or more PEM blocks.\n 2. All PEM blocks must have the \"CERTIFICATE\" label, contain no headers, and the encoded data\n must be a BER-encoded ASN.1 Certificate structure as described in section 4 of RFC5280.\n 3. Non-PEM content may appear before or after the \"CERTIFICATE\" PEM blocks and is unvalidated,\n to allow for explanatory text as described in section 5.2 of RFC7468.\n\nIf more than one PEM block is present, and the definition of the requested spec.signerName does not indicate otherwise, the first block is the issued certificate, and subsequent blocks should be treated as intermediate certificates and presented in TLS handshakes.\n\nThe certificate is encoded in PEM format.\n\nWhen serialized as JSON or YAML, the data is additionally base64-encoded, so it consists of:\n\n base64(\n -----BEGIN CERTIFICATE-----\n ...\n -----END CERTIFICATE-----\n )", "format": "byte", - "type": "string", - "x-kubernetes-list-type": "atomic" + "type": "string" }, "conditions": { "description": "conditions applied to the request. Known conditions are \"Approved\", \"Denied\", and \"Failed\".", @@ -700,7 +698,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -963,8 +961,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1394,7 +1391,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__certificates.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__certificates.k8s.io__v1alpha1_openapi.json index 1efa34f31a57d..19759ae02dd9e 100644 --- a/api/openapi-spec/v3/apis__certificates.k8s.io__v1alpha1_openapi.json +++ b/api/openapi-spec/v3/apis__certificates.k8s.io__v1alpha1_openapi.json @@ -106,222 +106,6 @@ ], "type": "object" }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequest": { - "description": "PodCertificateRequest encodes a pod requesting a certificate from a given signer.\n\nKubelets use this API to implement podCertificate projected volumes", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - } - ], - "default": {}, - "description": "metadata contains the object metadata." - }, - "spec": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestSpec" - } - ], - "default": {}, - "description": "spec contains the details about the certificate being requested." - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestStatus" - } - ], - "default": {}, - "description": "status contains the issued certificate, and a standard set of conditions." - } - }, - "required": [ - "spec" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestList": { - "description": "PodCertificateRequestList is a collection of PodCertificateRequest objects", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "items is a collection of PodCertificateRequest objects", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - ], - "default": {} - }, - "type": "array" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ], - "default": {}, - "description": "metadata contains the list metadata." - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequestList", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestSpec": { - "description": "PodCertificateRequestSpec describes the certificate request. All fields are immutable after creation.", - "properties": { - "maxExpirationSeconds": { - "default": 86400, - "description": "maxExpirationSeconds is the maximum lifetime permitted for the certificate.\n\nIf omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver will reject values shorter than 3600 (1 hour). The maximum allowable value is 7862400 (91 days).\n\nThe signer implementation is then free to issue a certificate with any lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600 seconds (1 hour). This constraint is enforced by kube-apiserver. `kubernetes.io` signers will never issue certificates with a lifetime longer than 24 hours.", - "format": "int32", - "type": "integer" - }, - "nodeName": { - "default": "", - "description": "nodeName is the name of the node the pod is assigned to.", - "type": "string" - }, - "nodeUID": { - "default": "", - "description": "nodeUID is the UID of the node the pod is assigned to.", - "type": "string" - }, - "pkixPublicKey": { - "description": "pkixPublicKey is the PKIX-serialized public key the signer will issue the certificate to.\n\nThe key must be one of RSA3072, RSA4096, ECDSAP256, ECDSAP384, ECDSAP521, or ED25519. Note that this list may be expanded in the future.\n\nSigner implementations do not need to support all key types supported by kube-apiserver and kubelet. If a signer does not support the key type used for a given PodCertificateRequest, it must deny the request by setting a status.conditions entry with a type of \"Denied\" and a reason of \"UnsupportedKeyType\". It may also suggest a key type that it does support in the message field.", - "format": "byte", - "type": "string" - }, - "podName": { - "default": "", - "description": "podName is the name of the pod into which the certificate will be mounted.", - "type": "string" - }, - "podUID": { - "default": "", - "description": "podUID is the UID of the pod into which the certificate will be mounted.", - "type": "string" - }, - "proofOfPossession": { - "description": "proofOfPossession proves that the requesting kubelet holds the private key corresponding to pkixPublicKey.\n\nIt is contructed by signing the ASCII bytes of the pod's UID using `pkixPublicKey`.\n\nkube-apiserver validates the proof of possession during creation of the PodCertificateRequest.\n\nIf the key is an RSA key, then the signature is over the ASCII bytes of the pod UID, using RSASSA-PSS from RFC 8017 (as implemented by the golang function crypto/rsa.SignPSS with nil options).\n\nIf the key is an ECDSA key, then the signature is as described by [SEC 1, Version 2.0](https://www.secg.org/sec1-v2.pdf) (as implemented by the golang library function crypto/ecdsa.SignASN1)\n\nIf the key is an ED25519 key, the the signature is as described by the [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by the golang library crypto/ed25519.Sign).", - "format": "byte", - "type": "string" - }, - "serviceAccountName": { - "default": "", - "description": "serviceAccountName is the name of the service account the pod is running as.", - "type": "string" - }, - "serviceAccountUID": { - "default": "", - "description": "serviceAccountUID is the UID of the service account the pod is running as.", - "type": "string" - }, - "signerName": { - "default": "", - "description": "signerName indicates the requested signer.\n\nAll signer names beginning with `kubernetes.io` are reserved for use by the Kubernetes project. There is currently one well-known signer documented by the Kubernetes project, `kubernetes.io/kube-apiserver-client-pod`, which will issue client certificates understood by kube-apiserver. It is currently unimplemented.", - "type": "string" - } - }, - "required": [ - "signerName", - "podName", - "podUID", - "serviceAccountName", - "serviceAccountUID", - "nodeName", - "nodeUID", - "pkixPublicKey", - "proofOfPossession" - ], - "type": "object" - }, - "io.k8s.api.certificates.v1alpha1.PodCertificateRequestStatus": { - "description": "PodCertificateRequestStatus describes the status of the request, and holds the certificate data if the request is issued.", - "properties": { - "beginRefreshAt": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "beginRefreshAt is the time at which the kubelet should begin trying to refresh the certificate. This field is set via the /status subresource, and must be set at the same time as certificateChain. Once populated, this field is immutable.\n\nThis field is only a hint. Kubelet may start refreshing before or after this time if necessary." - }, - "certificateChain": { - "description": "certificateChain is populated with an issued certificate by the signer. This field is set via the /status subresource. Once populated, this field is immutable.\n\nIf the certificate signing request is denied, a condition of type \"Denied\" is added and this field remains empty. If the signer cannot issue the certificate, a condition of type \"Failed\" is added and this field remains empty.\n\nValidation requirements:\n 1. certificateChain must consist of one or more PEM-formatted certificates.\n 2. Each entry must be a valid PEM-wrapped, DER-encoded ASN.1 Certificate as\n described in section 4 of RFC5280.\n\nIf more than one block is present, and the definition of the requested spec.signerName does not indicate otherwise, the first block is the issued certificate, and subsequent blocks should be treated as intermediate certificates and presented in TLS handshakes. When projecting the chain into a pod volume, kubelet will drop any data in-between the PEM blocks, as well as any PEM block headers.", - "type": "string" - }, - "conditions": { - "description": "conditions applied to the request.\n\nThe types \"Issued\", \"Denied\", and \"Failed\" have special handling. At most one of these conditions may be present, and they must have status \"True\".\n\nIf the request is denied with `Reason=UnsupportedKeyType`, the signer may suggest a key type that will work in the message field.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "type" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge" - }, - "notAfter": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "notAfter is the time at which the certificate expires. The value must be the same as the notAfter value in the leaf certificate in certificateChain. This field is set via the /status subresource. Once populated, it is immutable. The signer must set this field at the same time it sets certificateChain." - }, - "notBefore": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "notBefore is the time at which the certificate becomes valid. The value must be the same as the notBefore value in the leaf certificate in certificateChain. This field is set via the /status subresource. Once populated, it is immutable. The signer must set this field at the same time it sets certificateChain." - } - }, - "type": "object" - }, "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { "description": "APIResource specifies the name of a resource and whether it is namespaced.", "properties": { @@ -436,52 +220,6 @@ } ] }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Condition": { - "description": "Condition contains details for one aspect of the current state of this API Resource.", - "properties": { - "lastTransitionTime": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable." - }, - "message": { - "default": "", - "description": "message is a human readable message indicating details about the transition. This may be an empty string.", - "type": "string" - }, - "observedGeneration": { - "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", - "format": "int64", - "type": "integer" - }, - "reason": { - "default": "", - "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.", - "type": "string" - }, - "status": { - "default": "", - "description": "status of the condition, one of True, False, Unknown.", - "type": "string" - }, - "type": { - "default": "", - "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", - "type": "string" - } - }, - "required": [ - "type", - "status", - "lastTransitionTime", - "reason", - "message" - ], - "type": "object" - }, "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { "description": "DeleteOptions may be provided when deleting an API object.", "properties": { @@ -838,7 +576,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1101,8 +839,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1532,7 +1269,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2494,1781 +2231,50 @@ } } }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests": { - "delete": { - "description": "delete collection of PodCertificateRequest", - "operationId": "deleteCertificatesV1alpha1CollectionNamespacedPodCertificateRequest", - "parameters": [ - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "deletecollection", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "get": { - "description": "list or watch objects of kind PodCertificateRequest", - "operationId": "listCertificatesV1alpha1NamespacedPodCertificateRequest", - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "list", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "object name and auth scope, such as for teams and projects", - "in": "path", - "name": "namespace", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "post": { - "description": "create a PodCertificateRequest", - "operationId": "createCertificatesV1alpha1NamespacedPodCertificateRequest", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Created" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "post", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - } - }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests/{name}": { - "delete": { - "description": "delete a PodCertificateRequest", - "operationId": "deleteCertificatesV1alpha1NamespacedPodCertificateRequest", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "OK" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "delete", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "get": { - "description": "read the specified PodCertificateRequest", - "operationId": "readCertificatesV1alpha1NamespacedPodCertificateRequest", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "get", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "name of the PodCertificateRequest", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "object name and auth scope, such as for teams and projects", - "in": "path", - "name": "namespace", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "patch": { - "description": "partially update the specified PodCertificateRequest", - "operationId": "patchCertificatesV1alpha1NamespacedPodCertificateRequest", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - "in": "query", - "name": "force", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "application/apply-patch+cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/apply-patch+yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/strategic-merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "put": { - "description": "replace the specified PodCertificateRequest", - "operationId": "replaceCertificatesV1alpha1NamespacedPodCertificateRequest", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - } - }, - "/apis/certificates.k8s.io/v1alpha1/namespaces/{namespace}/podcertificaterequests/{name}/status": { - "get": { - "description": "read status of the specified PodCertificateRequest", - "operationId": "readCertificatesV1alpha1NamespacedPodCertificateRequestStatus", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "get", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "name of the PodCertificateRequest", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "object name and auth scope, such as for teams and projects", - "in": "path", - "name": "namespace", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "patch": { - "description": "partially update status of the specified PodCertificateRequest", - "operationId": "patchCertificatesV1alpha1NamespacedPodCertificateRequestStatus", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - "in": "query", - "name": "force", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "application/apply-patch+cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/apply-patch+yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/strategic-merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "put": { - "description": "replace status of the specified PodCertificateRequest", - "operationId": "replaceCertificatesV1alpha1NamespacedPodCertificateRequestStatus", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequest" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - } - }, - "/apis/certificates.k8s.io/v1alpha1/podcertificaterequests": { - "get": { - "description": "list or watch objects of kind PodCertificateRequest", - "operationId": "listCertificatesV1alpha1PodCertificateRequestForAllNamespaces", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.certificates.v1alpha1.PodCertificateRequestList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "list", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles": { - "get": { - "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1ClusterTrustBundleList", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles/{name}": { - "get": { - "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1alpha1ClusterTrustBundle", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "watch", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "name of the ClusterTrustBundle", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/namespaces/{namespace}/podcertificaterequests": { - "get": { - "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1NamespacedPodCertificateRequestList", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" + "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles": { + "get": { + "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1alpha1ClusterTrustBundleList", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" }, "401": { "description": "Unauthorized" @@ -4280,7 +2286,7 @@ "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", + "kind": "ClusterTrustBundle", "version": "v1alpha1" } }, @@ -4330,16 +2336,6 @@ "uniqueItems": true } }, - { - "description": "object name and auth scope, such as for teams and projects", - "in": "path", - "name": "namespace", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, { "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", "in": "query", @@ -4396,10 +2392,10 @@ } ] }, - "/apis/certificates.k8s.io/v1alpha1/watch/namespaces/{namespace}/podcertificaterequests/{name}": { + "/apis/certificates.k8s.io/v1alpha1/watch/clustertrustbundles/{name}": { "get": { - "description": "watch changes to an object of kind PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1alpha1NamespacedPodCertificateRequest", + "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1alpha1ClusterTrustBundle", "responses": { "200": { "content": { @@ -4451,7 +2447,7 @@ "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", + "kind": "ClusterTrustBundle", "version": "v1alpha1" } }, @@ -4502,7 +2498,7 @@ } }, { - "description": "name of the PodCertificateRequest", + "description": "name of the ClusterTrustBundle", "in": "path", "name": "name", "required": true, @@ -4511,177 +2507,6 @@ "uniqueItems": true } }, - { - "description": "object name and auth scope, such as for teams and projects", - "in": "path", - "name": "namespace", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/certificates.k8s.io/v1alpha1/watch/podcertificaterequests": { - "get": { - "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1alpha1PodCertificateRequestListForAllNamespaces", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "certificates_v1alpha1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "certificates.k8s.io", - "kind": "PodCertificateRequest", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, { "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", "in": "query", diff --git a/api/openapi-spec/v3/apis__certificates.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__certificates.k8s.io__v1beta1_openapi.json index 45bbb790eae56..4f39f990d9341 100644 --- a/api/openapi-spec/v3/apis__certificates.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__certificates.k8s.io__v1beta1_openapi.json @@ -106,6 +106,230 @@ ], "type": "object" }, + "io.k8s.api.certificates.v1beta1.PodCertificateRequest": { + "description": "PodCertificateRequest encodes a pod requesting a certificate from a given signer.\n\nKubelets use this API to implement podCertificate projected volumes", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ], + "default": {}, + "description": "metadata contains the object metadata." + }, + "spec": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestSpec" + } + ], + "default": {}, + "description": "spec contains the details about the certificate being requested." + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestStatus" + } + ], + "default": {}, + "description": "status contains the issued certificate, and a standard set of conditions." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.PodCertificateRequestList": { + "description": "PodCertificateRequestList is a collection of PodCertificateRequest objects", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "items is a collection of PodCertificateRequest objects", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + ], + "default": {} + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ], + "default": {}, + "description": "metadata contains the list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequestList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.certificates.v1beta1.PodCertificateRequestSpec": { + "description": "PodCertificateRequestSpec describes the certificate request. All fields are immutable after creation.", + "properties": { + "maxExpirationSeconds": { + "default": 86400, + "description": "maxExpirationSeconds is the maximum lifetime permitted for the certificate.\n\nIf omitted, kube-apiserver will set it to 86400(24 hours). kube-apiserver will reject values shorter than 3600 (1 hour). The maximum allowable value is 7862400 (91 days).\n\nThe signer implementation is then free to issue a certificate with any lifetime *shorter* than MaxExpirationSeconds, but no shorter than 3600 seconds (1 hour). This constraint is enforced by kube-apiserver. `kubernetes.io` signers will never issue certificates with a lifetime longer than 24 hours.", + "format": "int32", + "type": "integer" + }, + "nodeName": { + "default": "", + "description": "nodeName is the name of the node the pod is assigned to.", + "type": "string" + }, + "nodeUID": { + "default": "", + "description": "nodeUID is the UID of the node the pod is assigned to.", + "type": "string" + }, + "pkixPublicKey": { + "description": "pkixPublicKey is the PKIX-serialized public key the signer will issue the certificate to.\n\nThe key must be one of RSA3072, RSA4096, ECDSAP256, ECDSAP384, ECDSAP521, or ED25519. Note that this list may be expanded in the future.\n\nSigner implementations do not need to support all key types supported by kube-apiserver and kubelet. If a signer does not support the key type used for a given PodCertificateRequest, it must deny the request by setting a status.conditions entry with a type of \"Denied\" and a reason of \"UnsupportedKeyType\". It may also suggest a key type that it does support in the message field.", + "format": "byte", + "type": "string" + }, + "podName": { + "default": "", + "description": "podName is the name of the pod into which the certificate will be mounted.", + "type": "string" + }, + "podUID": { + "default": "", + "description": "podUID is the UID of the pod into which the certificate will be mounted.", + "type": "string" + }, + "proofOfPossession": { + "description": "proofOfPossession proves that the requesting kubelet holds the private key corresponding to pkixPublicKey.\n\nIt is contructed by signing the ASCII bytes of the pod's UID using `pkixPublicKey`.\n\nkube-apiserver validates the proof of possession during creation of the PodCertificateRequest.\n\nIf the key is an RSA key, then the signature is over the ASCII bytes of the pod UID, using RSASSA-PSS from RFC 8017 (as implemented by the golang function crypto/rsa.SignPSS with nil options).\n\nIf the key is an ECDSA key, then the signature is as described by [SEC 1, Version 2.0](https://www.secg.org/sec1-v2.pdf) (as implemented by the golang library function crypto/ecdsa.SignASN1)\n\nIf the key is an ED25519 key, the the signature is as described by the [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by the golang library crypto/ed25519.Sign).", + "format": "byte", + "type": "string" + }, + "serviceAccountName": { + "default": "", + "description": "serviceAccountName is the name of the service account the pod is running as.", + "type": "string" + }, + "serviceAccountUID": { + "default": "", + "description": "serviceAccountUID is the UID of the service account the pod is running as.", + "type": "string" + }, + "signerName": { + "default": "", + "description": "signerName indicates the requested signer.\n\nAll signer names beginning with `kubernetes.io` are reserved for use by the Kubernetes project. There is currently one well-known signer documented by the Kubernetes project, `kubernetes.io/kube-apiserver-client-pod`, which will issue client certificates understood by kube-apiserver. It is currently unimplemented.", + "type": "string" + }, + "unverifiedUserAnnotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "unverifiedUserAnnotations allow pod authors to pass additional information to the signer implementation. Kubernetes does not restrict or validate this metadata in any way.\n\nEntries are subject to the same validation as object metadata annotations, with the addition that all keys must be domain-prefixed. No restrictions are placed on values, except an overall size limitation on the entire field.\n\nSigners should document the keys and values they support. Signers should deny requests that contain keys they do not recognize.", + "type": "object" + } + }, + "required": [ + "signerName", + "podName", + "podUID", + "serviceAccountName", + "serviceAccountUID", + "nodeName", + "nodeUID", + "pkixPublicKey", + "proofOfPossession" + ], + "type": "object" + }, + "io.k8s.api.certificates.v1beta1.PodCertificateRequestStatus": { + "description": "PodCertificateRequestStatus describes the status of the request, and holds the certificate data if the request is issued.", + "properties": { + "beginRefreshAt": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "beginRefreshAt is the time at which the kubelet should begin trying to refresh the certificate. This field is set via the /status subresource, and must be set at the same time as certificateChain. Once populated, this field is immutable.\n\nThis field is only a hint. Kubelet may start refreshing before or after this time if necessary." + }, + "certificateChain": { + "description": "certificateChain is populated with an issued certificate by the signer. This field is set via the /status subresource. Once populated, this field is immutable.\n\nIf the certificate signing request is denied, a condition of type \"Denied\" is added and this field remains empty. If the signer cannot issue the certificate, a condition of type \"Failed\" is added and this field remains empty.\n\nValidation requirements:\n 1. certificateChain must consist of one or more PEM-formatted certificates.\n 2. Each entry must be a valid PEM-wrapped, DER-encoded ASN.1 Certificate as\n described in section 4 of RFC5280.\n\nIf more than one block is present, and the definition of the requested spec.signerName does not indicate otherwise, the first block is the issued certificate, and subsequent blocks should be treated as intermediate certificates and presented in TLS handshakes. When projecting the chain into a pod volume, kubelet will drop any data in-between the PEM blocks, as well as any PEM block headers.", + "type": "string" + }, + "conditions": { + "description": "conditions applied to the request.\n\nThe types \"Issued\", \"Denied\", and \"Failed\" have special handling. At most one of these conditions may be present, and they must have status \"True\".\n\nIf the request is denied with `Reason=UnsupportedKeyType`, the signer may suggest a key type that will work in the message field.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "notAfter": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "notAfter is the time at which the certificate expires. The value must be the same as the notAfter value in the leaf certificate in certificateChain. This field is set via the /status subresource. Once populated, it is immutable. The signer must set this field at the same time it sets certificateChain." + }, + "notBefore": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "notBefore is the time at which the certificate becomes valid. The value must be the same as the notBefore value in the leaf certificate in certificateChain. This field is set via the /status subresource. Once populated, it is immutable. The signer must set this field at the same time it sets certificateChain." + } + }, + "type": "object" + }, "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { "description": "APIResource specifies the name of a resource and whether it is namespaced.", "properties": { @@ -220,6 +444,52 @@ } ] }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Condition": { + "description": "Condition contains details for one aspect of the current state of this API Resource.", + "properties": { + "lastTransitionTime": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable." + }, + "message": { + "default": "", + "description": "message is a human readable message indicating details about the transition. This may be an empty string.", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, + "reason": { + "default": "", + "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.", + "type": "string" + }, + "status": { + "default": "", + "description": "status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "default": "", + "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", + "type": "string" + } + }, + "required": [ + "type", + "status", + "lastTransitionTime", + "reason", + "message" + ], + "type": "object" + }, "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { "description": "DeleteOptions may be provided when deleting an API object.", "properties": { @@ -576,7 +846,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -839,8 +1109,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1270,7 +1539,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2232,54 +2501,1785 @@ } } }, - "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles": { - "get": { - "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchCertificatesV1beta1ClusterTrustBundleList", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests": { + "delete": { + "description": "delete collection of PodCertificateRequest", + "operationId": "deleteCertificatesV1beta1CollectionNamespacedPodCertificateRequest", + "parameters": [ + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } }, - "401": { - "description": "Unauthorized" - } + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "get": { + "description": "list or watch objects of kind PodCertificateRequest", + "operationId": "listCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "post": { + "description": "create a PodCertificateRequest", + "operationId": "createCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Created" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + } + }, + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests/{name}": { + "delete": { + "description": "delete a PodCertificateRequest", + "operationId": "deleteCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "get": { + "description": "read the specified PodCertificateRequest", + "operationId": "readCertificatesV1beta1NamespacedPodCertificateRequest", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "name of the PodCertificateRequest", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update the specified PodCertificateRequest", + "operationId": "patchCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "put": { + "description": "replace the specified PodCertificateRequest", + "operationId": "replaceCertificatesV1beta1NamespacedPodCertificateRequest", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + } + }, + "/apis/certificates.k8s.io/v1beta1/namespaces/{namespace}/podcertificaterequests/{name}/status": { + "get": { + "description": "read status of the specified PodCertificateRequest", + "operationId": "readCertificatesV1beta1NamespacedPodCertificateRequestStatus", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "name of the PodCertificateRequest", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update status of the specified PodCertificateRequest", + "operationId": "patchCertificatesV1beta1NamespacedPodCertificateRequestStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "put": { + "description": "replace status of the specified PodCertificateRequest", + "operationId": "replaceCertificatesV1beta1NamespacedPodCertificateRequestStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequest" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + } + }, + "/apis/certificates.k8s.io/v1beta1/podcertificaterequests": { + "get": { + "description": "list or watch objects of kind PodCertificateRequest", + "operationId": "listCertificatesV1beta1PodCertificateRequestForAllNamespaces", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.certificates.v1beta1.PodCertificateRequestList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles": { + "get": { + "description": "watch individual changes to a list of ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1ClusterTrustBundleList", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles/{name}": { + "get": { + "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1beta1ClusterTrustBundle", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "ClusterTrustBundle", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "name of the ClusterTrustBundle", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/namespaces/{namespace}/podcertificaterequests": { + "get": { + "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1NamespacedPodCertificateRequestList", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } }, "tags": [ "certificates_v1beta1" @@ -2287,7 +4287,7 @@ "x-kubernetes-action": "watchlist", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, @@ -2337,6 +4337,16 @@ "uniqueItems": true } }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, { "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", "in": "query", @@ -2393,10 +4403,10 @@ } ] }, - "/apis/certificates.k8s.io/v1beta1/watch/clustertrustbundles/{name}": { + "/apis/certificates.k8s.io/v1beta1/watch/namespaces/{namespace}/podcertificaterequests/{name}": { "get": { - "description": "watch changes to an object of kind ClusterTrustBundle. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchCertificatesV1beta1ClusterTrustBundle", + "description": "watch changes to an object of kind PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchCertificatesV1beta1NamespacedPodCertificateRequest", "responses": { "200": { "content": { @@ -2448,7 +4458,7 @@ "x-kubernetes-action": "watch", "x-kubernetes-group-version-kind": { "group": "certificates.k8s.io", - "kind": "ClusterTrustBundle", + "kind": "PodCertificateRequest", "version": "v1beta1" } }, @@ -2499,7 +4509,7 @@ } }, { - "description": "name of the ClusterTrustBundle", + "description": "name of the PodCertificateRequest", "in": "path", "name": "name", "required": true, @@ -2508,6 +4518,177 @@ "uniqueItems": true } }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/certificates.k8s.io/v1beta1/watch/podcertificaterequests": { + "get": { + "description": "watch individual changes to a list of PodCertificateRequest. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchCertificatesV1beta1PodCertificateRequestListForAllNamespaces", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "certificates_v1beta1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "certificates.k8s.io", + "kind": "PodCertificateRequest", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, { "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", "in": "query", diff --git a/api/openapi-spec/v3/apis__coordination.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__coordination.k8s.io__v1_openapi.json index 448c4a9489f59..1bcf968e72352 100644 --- a/api/openapi-spec/v3/apis__coordination.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__coordination.k8s.io__v1_openapi.json @@ -599,7 +599,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -867,8 +867,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1298,7 +1297,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__coordination.k8s.io__v1alpha2_openapi.json b/api/openapi-spec/v3/apis__coordination.k8s.io__v1alpha2_openapi.json index 6b694293851db..913b8d3715294 100644 --- a/api/openapi-spec/v3/apis__coordination.k8s.io__v1alpha2_openapi.json +++ b/api/openapi-spec/v3/apis__coordination.k8s.io__v1alpha2_openapi.json @@ -600,7 +600,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -868,8 +868,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1299,7 +1298,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__coordination.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__coordination.k8s.io__v1beta1_openapi.json index 6a1f8bd4136b3..d062175244bb1 100644 --- a/api/openapi-spec/v3/apis__coordination.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__coordination.k8s.io__v1beta1_openapi.json @@ -600,7 +600,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -868,8 +868,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1299,7 +1298,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__discovery.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__discovery.k8s.io__v1_openapi.json index 8ca040b7709a4..64d668dd8031d 100644 --- a/api/openapi-spec/v3/apis__discovery.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__discovery.k8s.io__v1_openapi.json @@ -121,7 +121,7 @@ "description": "EndpointHints provides hints describing how an endpoint should be consumed.", "properties": { "forNodes": { - "description": "forNodes indicates the node(s) this endpoint should be consumed by when using topology aware routing. May contain a maximum of 8 entries. This is an Alpha feature and is only used when the PreferSameTrafficDistribution feature gate is enabled.", + "description": "forNodes indicates the node(s) this endpoint should be consumed by when using topology aware routing. May contain a maximum of 8 entries.", "items": { "allOf": [ { @@ -781,7 +781,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1044,8 +1044,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1475,7 +1474,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__events.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__events.k8s.io__v1_openapi.json index 051bf84f328a0..66270d22eccee 100644 --- a/api/openapi-spec/v3/apis__events.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__events.k8s.io__v1_openapi.json @@ -709,7 +709,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -977,8 +977,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1408,7 +1407,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__flowcontrol.apiserver.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__flowcontrol.apiserver.k8s.io__v1_openapi.json index 830ce2752b42e..fb04fcac3dfa4 100644 --- a/api/openapi-spec/v3/apis__flowcontrol.apiserver.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__flowcontrol.apiserver.k8s.io__v1_openapi.json @@ -1200,7 +1200,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1463,8 +1463,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1894,7 +1893,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__internal.apiserver.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__internal.apiserver.k8s.io__v1alpha1_openapi.json index 202f941fdde24..29a9331d6bda5 100644 --- a/api/openapi-spec/v3/apis__internal.apiserver.k8s.io__v1alpha1_openapi.json +++ b/api/openapi-spec/v3/apis__internal.apiserver.k8s.io__v1alpha1_openapi.json @@ -690,7 +690,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -953,8 +953,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1384,7 +1383,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__networking.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__networking.k8s.io__v1_openapi.json index 6ff4af4efeee0..cc4bdb4a2fb5e 100644 --- a/api/openapi-spec/v3/apis__networking.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__networking.k8s.io__v1_openapi.json @@ -1574,7 +1574,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1894,8 +1894,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2325,7 +2324,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__networking.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__networking.k8s.io__v1beta1_openapi.json index dcb2896679058..5b05e1142bcf3 100644 --- a/api/openapi-spec/v3/apis__networking.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__networking.k8s.io__v1beta1_openapi.json @@ -776,7 +776,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1039,8 +1039,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1470,7 +1469,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__node.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__node.k8s.io__v1_openapi.json index 91a759a67dc50..08fd541701893 100644 --- a/api/openapi-spec/v3/apis__node.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__node.k8s.io__v1_openapi.json @@ -13,7 +13,7 @@ "type": "string" }, "operator": { - "description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + "description": "Operator represents a key's relationship to the value. Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).", "type": "string" }, "tolerationSeconds": { @@ -649,7 +649,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -912,8 +912,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1343,7 +1342,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__policy__v1_openapi.json b/api/openapi-spec/v3/apis__policy__v1_openapi.json index 66d0497458421..bd1134c93f57a 100644 --- a/api/openapi-spec/v3/apis__policy__v1_openapi.json +++ b/api/openapi-spec/v3/apis__policy__v1_openapi.json @@ -711,7 +711,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1031,8 +1031,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1462,7 +1461,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__rbac.authorization.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__rbac.authorization.k8s.io__v1_openapi.json index c30bb6c3262cb..525506b471ed4 100644 --- a/api/openapi-spec/v3/apis__rbac.authorization.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__rbac.authorization.k8s.io__v1_openapi.json @@ -984,7 +984,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -1304,8 +1304,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1735,7 +1734,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__resource.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__resource.k8s.io__v1_openapi.json index 7b6a4c9ad9e0d..03eadd66258fc 100644 --- a/api/openapi-spec/v3/apis__resource.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__resource.k8s.io__v1_openapi.json @@ -120,7 +120,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -287,7 +287,7 @@ "type": "object" }, "io.k8s.api.resource.v1.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { @@ -298,7 +298,7 @@ ], "default": {} }, - "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters in all sets is 32.", + "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters is 32.", "type": "object" }, "name": { @@ -371,7 +371,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "allOf": [ { @@ -401,7 +401,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "allOf": [ { @@ -775,7 +775,7 @@ ], "default": {} }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -859,7 +859,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -980,7 +980,7 @@ "properties": { "effect": { "default": "", - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -1124,7 +1124,7 @@ "properties": { "driver": { "default": "", - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -1571,7 +1571,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "allOf": [ { @@ -1585,7 +1585,7 @@ }, "driver": { "default": "", - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -1614,7 +1614,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "allOf": [ { @@ -2160,7 +2160,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2423,8 +2423,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2854,7 +2853,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json b/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json index 94ce67310b565..1f20120ccae4e 100644 --- a/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json +++ b/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json @@ -1,40 +1,12 @@ { "components": { "schemas": { - "io.k8s.api.resource.v1alpha3.CELDeviceSelector": { - "description": "CELDeviceSelector contains a CEL expression for selecting a device.", - "properties": { - "expression": { - "default": "", - "description": "Expression is a CEL expression which evaluates a single device. It must evaluate to true when the device under consideration satisfies the desired criteria, and false when it does not. Any other result is an error and causes allocation of devices to abort.\n\nThe expression's input is an object named \"device\", which carries the following properties:\n - driver (string): the name of the driver which defines this device.\n - attributes (map[string]object): the device's attributes, grouped by prefix\n (e.g. device.attributes[\"dra.example.com\"] evaluates to an object with all\n of the attributes which were prefixed by \"dra.example.com\".\n - capacity (map[string]object): the device's capacities, grouped by prefix.\n\nExample: Consider a device with driver=\"dra.example.com\", which exposes two attributes named \"model\" and \"ext.example.com/family\" and which exposes one capacity named \"modules\". This input to this expression would have the following fields:\n\n device.driver\n device.attributes[\"dra.example.com\"].model\n device.attributes[\"ext.example.com\"].family\n device.capacity[\"dra.example.com\"].modules\n\nThe device.driver field can be used to check for a specific driver, either as a high-level precondition (i.e. you only want to consider devices from this driver) or as part of a multi-clause expression that is meant to consider devices from different drivers.\n\nThe value type of each attribute is defined by the device definition, and users who write these expressions must consult the documentation for their specific drivers. The value type of each capacity is Quantity.\n\nIf an unknown prefix is used as a lookup in either device.attributes or device.capacity, an empty map will be returned. Any reference to an unknown field will cause an evaluation error and allocation to abort.\n\nA robust expression should check for the existence of attributes before referencing them.\n\nFor ease of use, the cel.bind() function is enabled, and can be used to simplify expressions that access multiple attributes with the same domain. For example:\n\n cel.bind(dra, device.attributes[\"dra.example.com\"], dra.someBool && dra.anotherBool)\n\nThe length of the expression must be smaller or equal to 10 Ki. The cost of evaluating it is also limited based on the estimated number of logical steps.", - "type": "string" - } - }, - "required": [ - "expression" - ], - "type": "object" - }, - "io.k8s.api.resource.v1alpha3.DeviceSelector": { - "description": "DeviceSelector must have exactly one field set.", - "properties": { - "cel": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.CELDeviceSelector" - } - ], - "description": "CEL contains a CEL expression for selecting a device." - } - }, - "type": "object" - }, "io.k8s.api.resource.v1alpha3.DeviceTaint": { "description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.", "properties": { "effect": { "default": "", - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -89,6 +61,15 @@ ], "default": {}, "description": "Spec specifies the selector and one taint.\n\nChanging the spec automatically increments the metadata.generation number." + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus" + } + ], + "default": {}, + "description": "Status provides information about what was requested in the spec." } }, "required": [ @@ -157,7 +138,7 @@ "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintSelector" } ], - "description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches." + "description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches." }, "taint": { "allOf": [ @@ -174,6 +155,30 @@ ], "type": "object" }, + "io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus": { + "description": "DeviceTaintRuleStatus provides information about an on-going pod eviction.", + "properties": { + "conditions": { + "description": "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object" + }, "io.k8s.api.resource.v1alpha3.DeviceTaintSelector": { "description": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.", "properties": { @@ -181,10 +186,6 @@ "description": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.", "type": "string" }, - "deviceClassName": { - "description": "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.", - "type": "string" - }, "driver": { "description": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.", "type": "string" @@ -192,19 +193,6 @@ "pool": { "description": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.", "type": "string" - }, - "selectors": { - "description": "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceSelector" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" } }, "type": "object" @@ -323,6 +311,52 @@ } ] }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Condition": { + "description": "Condition contains details for one aspect of the current state of this API Resource.", + "properties": { + "lastTransitionTime": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable." + }, + "message": { + "default": "", + "description": "message is a human readable message indicating details about the transition. This may be an empty string.", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, + "reason": { + "default": "", + "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.", + "type": "string" + }, + "status": { + "default": "", + "description": "status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "default": "", + "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", + "type": "string" + } + }, + "required": [ + "type", + "status", + "lastTransitionTime", + "reason", + "message" + ], + "type": "object" + }, "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { "description": "DeleteOptions may be provided when deleting an API object.", "properties": { @@ -679,7 +713,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -942,8 +976,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1373,7 +1406,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2335,6 +2368,315 @@ } } }, + "/apis/resource.k8s.io/v1alpha3/devicetaintrules/{name}/status": { + "get": { + "description": "read status of the specified DeviceTaintRule", + "operationId": "readResourceV1alpha3DeviceTaintRuleStatus", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + }, + "parameters": [ + { + "description": "name of the DeviceTaintRule", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update status of the specified DeviceTaintRule", + "operationId": "patchResourceV1alpha3DeviceTaintRuleStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + }, + "put": { + "description": "replace status of the specified DeviceTaintRule", + "operationId": "replaceResourceV1alpha3DeviceTaintRuleStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "resource_v1alpha3" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "resource.k8s.io", + "kind": "DeviceTaintRule", + "version": "v1alpha3" + } + } + }, "/apis/resource.k8s.io/v1alpha3/watch/devicetaintrules": { "get": { "description": "watch individual changes to a list of DeviceTaintRule. deprecated: use the 'watch' parameter with a list operation instead.", diff --git a/api/openapi-spec/v3/apis__resource.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__resource.k8s.io__v1beta1_openapi.json index 6a36dfaefa453..d3cf12a1f92a1 100644 --- a/api/openapi-spec/v3/apis__resource.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__resource.k8s.io__v1beta1_openapi.json @@ -120,7 +120,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -237,7 +237,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "allOf": [ { @@ -262,7 +262,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "allOf": [ { @@ -385,7 +385,7 @@ "type": "object" }, "io.k8s.api.resource.v1beta1.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { @@ -789,7 +789,7 @@ ], "default": {} }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -917,7 +917,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -1038,7 +1038,7 @@ "properties": { "effect": { "default": "", - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -1121,7 +1121,7 @@ "properties": { "driver": { "default": "", - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -1568,7 +1568,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "allOf": [ { @@ -1582,7 +1582,7 @@ }, "driver": { "default": "", - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -1611,7 +1611,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of SharedCounters is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "allOf": [ { @@ -2157,7 +2157,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2420,8 +2420,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2851,7 +2850,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__resource.k8s.io__v1beta2_openapi.json b/api/openapi-spec/v3/apis__resource.k8s.io__v1beta2_openapi.json index 91b13fd164ab4..ba43ec7c65ffb 100644 --- a/api/openapi-spec/v3/apis__resource.k8s.io__v1beta2_openapi.json +++ b/api/openapi-spec/v3/apis__resource.k8s.io__v1beta2_openapi.json @@ -120,7 +120,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "networkData": { @@ -287,7 +287,7 @@ "type": "object" }, "io.k8s.api.resource.v1beta2.CounterSet": { - "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourceSlice.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", + "description": "CounterSet defines a named set of counters that are available to be used by devices defined in the ResourcePool.\n\nThe counters are not allocatable by themselves, but can be referenced by devices. When a device is allocated, the portion of counters it uses will no longer be available for use by other devices.", "properties": { "counters": { "additionalProperties": { @@ -298,7 +298,7 @@ ], "default": {} }, - "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters in all sets is 32.", + "description": "Counters defines the set of counters for this CounterSet The name of each counter must be unique in that set and must be a DNS label.\n\nThe maximum number of counters is 32.", "type": "object" }, "name": { @@ -371,7 +371,7 @@ "type": "object" }, "consumesCounters": { - "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe total number of device counter consumption entries must be <= 32. In addition, the total number in the entire ResourceSlice must be <= 1024 (for example, 64 devices with 16 counters each).", + "description": "ConsumesCounters defines a list of references to sharedCounters and the set of counters that the device will consume from those counter sets.\n\nThere can only be a single entry per counterSet.\n\nThe maximum number of device counter consumptions per device is 2.", "items": { "allOf": [ { @@ -401,7 +401,7 @@ "description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set." }, "taints": { - "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", + "description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.", "items": { "allOf": [ { @@ -775,7 +775,7 @@ ], "default": {} }, - "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number counters in a device is 32. In addition, the maximum number of all counters in all devices is 1024 (for example, 64 devices with 16 counters each).", + "description": "Counters defines the counters that will be consumed by the device.\n\nThe maximum number of counters is 32.", "type": "object" } }, @@ -859,7 +859,7 @@ }, "driver": { "default": "", - "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "pool": { @@ -980,7 +980,7 @@ "properties": { "effect": { "default": "", - "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.", + "description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.", "type": "string" }, "key": { @@ -1124,7 +1124,7 @@ "properties": { "driver": { "default": "", - "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "description": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters.", "type": "string" }, "parameters": { @@ -1571,7 +1571,7 @@ "type": "boolean" }, "devices": { - "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.", + "description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints or consumes counters the limit is 64.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.", "items": { "allOf": [ { @@ -1585,7 +1585,7 @@ }, "driver": { "default": "", - "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. This field is immutable.", + "description": "Driver identifies the DRA driver providing the capacity information. A field selector can be used to list only ResourceSlice objects with a certain driver name.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver. It should use only lower case characters. This field is immutable.", "type": "string" }, "nodeName": { @@ -1614,7 +1614,7 @@ "description": "Pool describes the pool that this ResourceSlice belongs to." }, "sharedCounters": { - "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.", + "description": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the counter sets must be unique in the ResourcePool.\n\nOnly one of Devices and SharedCounters can be set in a ResourceSlice.\n\nThe maximum number of counter sets is 8.", "items": { "allOf": [ { @@ -2160,7 +2160,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2423,8 +2423,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -2854,7 +2853,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__scheduling.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1_openapi.json index a731c1571b16e..207485422ff51 100644 --- a/api/openapi-spec/v3/apis__scheduling.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1_openapi.json @@ -567,7 +567,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -830,8 +830,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1261,7 +1260,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha1_openapi.json new file mode 100644 index 0000000000000..75e6562b8caa4 --- /dev/null +++ b/api/openapi-spec/v3/apis__scheduling.k8s.io__v1alpha1_openapi.json @@ -0,0 +1,3033 @@ +{ + "components": { + "schemas": { + "io.k8s.api.scheduling.v1alpha1.BasicSchedulingPolicy": { + "description": "BasicSchedulingPolicy indicates that standard Kubernetes scheduling behavior should be used.", + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.GangSchedulingPolicy": { + "description": "GangSchedulingPolicy defines the parameters for gang scheduling.", + "properties": { + "minCount": { + "default": 0, + "description": "MinCount is the minimum number of pods that must be schedulable or scheduled at the same time for the scheduler to admit the entire group. It must be a positive integer.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "minCount" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.PodGroup": { + "description": "PodGroup represents a set of pods with a common scheduling policy.", + "properties": { + "name": { + "default": "", + "description": "Name is a unique identifier for the PodGroup within the Workload. It must be a DNS label. This field is immutable.", + "type": "string" + }, + "policy": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.PodGroupPolicy" + } + ], + "default": {}, + "description": "Policy defines the scheduling policy for this PodGroup." + } + }, + "required": [ + "name", + "policy" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.PodGroupPolicy": { + "description": "PodGroupPolicy defines the scheduling configuration for a PodGroup.", + "properties": { + "basic": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.BasicSchedulingPolicy" + } + ], + "description": "Basic specifies that the pods in this group should be scheduled using standard Kubernetes scheduling behavior." + }, + "gang": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.GangSchedulingPolicy" + } + ], + "description": "Gang specifies that the pods in this group should be scheduled using all-or-nothing semantics." + } + }, + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.TypedLocalObjectReference": { + "description": "TypedLocalObjectReference allows to reference typed object inside the same namespace.", + "properties": { + "apiGroup": { + "description": "APIGroup is the group for the resource being referenced. If APIGroup is empty, the specified Kind must be in the core API group. For any other third-party types, setting APIGroup is required. It must be a DNS subdomain.", + "type": "string" + }, + "kind": { + "default": "", + "description": "Kind is the type of resource being referenced. It must be a path segment name.", + "type": "string" + }, + "name": { + "default": "", + "description": "Name is the name of resource being referenced. It must be a path segment name.", + "type": "string" + } + }, + "required": [ + "kind", + "name" + ], + "type": "object" + }, + "io.k8s.api.scheduling.v1alpha1.Workload": { + "description": "Workload allows for expressing scheduling constraints that should be used when managing lifecycle of workloads from scheduling perspective, including scheduling, preemption, eviction and other phases.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ], + "default": {}, + "description": "Standard object's metadata. Name must be a DNS subdomain." + }, + "spec": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadSpec" + } + ], + "default": {}, + "description": "Spec defines the desired behavior of a Workload." + } + }, + "required": [ + "spec" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.WorkloadList": { + "description": "WorkloadList contains a list of Workload resources.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of Workloads.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + ], + "default": {} + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ], + "default": {}, + "description": "Standard list metadata." + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "scheduling.k8s.io", + "kind": "WorkloadList", + "version": "v1alpha1" + } + ] + }, + "io.k8s.api.scheduling.v1alpha1.WorkloadSpec": { + "description": "WorkloadSpec defines the desired state of a Workload.", + "properties": { + "controllerRef": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.TypedLocalObjectReference" + } + ], + "description": "ControllerRef is an optional reference to the controlling object, such as a Deployment or Job. This field is intended for use by tools like CLIs to provide a link back to the original workload definition. When set, it cannot be changed." + }, + "podGroups": { + "description": "PodGroups is the list of pod groups that make up the Workload. The maximum number of pod groups is 8. This field is immutable.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.PodGroup" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + } + }, + "required": [ + "podGroups" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "default": "", + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string" + }, + "name": { + "default": "", + "description": "name is the plural name of the resource.", + "type": "string" + }, + "namespaced": { + "default": false, + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean" + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "singularName": { + "default": "", + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "items": { + "default": "", + "type": "string" + }, + "type": "array" + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + }, + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "groupVersion": { + "default": "", + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "groupVersion", + "resources" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIResourceList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "dryRun": { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "gracePeriodSeconds": { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "format": "int64", + "type": "integer" + }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "orphanDependents": { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "type": "boolean" + }, + "preconditions": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" + } + ], + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." + }, + "propagationPolicy": { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha2" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha3" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "storagemigration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { + "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "format": "int64", + "type": "integer" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" + } + ], + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type." + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", + "type": "string" + }, + "time": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over." + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "properties": { + "annotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", + "type": "object" + }, + "creationTimestamp": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "format": "int64", + "type": "integer" + }, + "deletionTimestamp": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "format": "int64", + "type": "integer" + }, + "labels": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", + "type": "object" + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "properties": { + "apiVersion": { + "default": "", + "description": "API version of the referent.", + "type": "string" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "default": "", + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "default": "", + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "uid": { + "default": "", + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "format": "int32", + "type": "integer" + }, + "details": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" + } + ], + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ], + "default": {}, + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + }, + "reason": { + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + "type": "string" + }, + "status": { + "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Status", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "properties": { + "field": { + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + "format": "int32", + "type": "integer" + }, + "uid": { + "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { + "description": "Event represents a single event to a watched resource.", + "properties": { + "object": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension" + } + ], + "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." + }, + "type": { + "default": "", + "type": "string" + } + }, + "required": [ + "type", + "object" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha2" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha3" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "storagemigration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.runtime.RawExtension": { + "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + "type": "object" + } + }, + "securitySchemes": { + "BearerToken": { + "description": "Bearer Token authentication", + "in": "header", + "name": "authorization", + "type": "apiKey" + } + } + }, + "info": { + "title": "Kubernetes", + "version": "unversioned" + }, + "openapi": "3.0.0", + "paths": { + "/apis/scheduling.k8s.io/v1alpha1/": { + "get": { + "description": "get available resources", + "operationId": "getSchedulingV1alpha1APIResources", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ] + } + }, + "/apis/scheduling.k8s.io/v1alpha1/namespaces/{namespace}/workloads": { + "delete": { + "description": "delete collection of Workload", + "operationId": "deleteSchedulingV1alpha1CollectionNamespacedWorkload", + "parameters": [ + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "get": { + "description": "list or watch objects of kind Workload", + "operationId": "listSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "post": { + "description": "create a Workload", + "operationId": "createSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "Created" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + } + }, + "/apis/scheduling.k8s.io/v1alpha1/namespaces/{namespace}/workloads/{name}": { + "delete": { + "description": "delete a Workload", + "operationId": "deleteSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "get": { + "description": "read the specified Workload", + "operationId": "readSchedulingV1alpha1NamespacedWorkload", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "name of the Workload", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update the specified Workload", + "operationId": "patchSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "put": { + "description": "replace the specified Workload", + "operationId": "replaceSchedulingV1alpha1NamespacedWorkload", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.Workload" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + } + }, + "/apis/scheduling.k8s.io/v1alpha1/watch/namespaces/{namespace}/workloads": { + "get": { + "description": "watch individual changes to a list of Workload. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchSchedulingV1alpha1NamespacedWorkloadList", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/scheduling.k8s.io/v1alpha1/watch/namespaces/{namespace}/workloads/{name}": { + "get": { + "description": "watch changes to an object of kind Workload. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchSchedulingV1alpha1NamespacedWorkload", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "name of the Workload", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "object name and auth scope, such as for teams and projects", + "in": "path", + "name": "namespace", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/scheduling.k8s.io/v1alpha1/watch/workloads": { + "get": { + "description": "watch individual changes to a list of Workload. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchSchedulingV1alpha1WorkloadListForAllNamespaces", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/scheduling.k8s.io/v1alpha1/workloads": { + "get": { + "description": "list or watch objects of kind Workload", + "operationId": "listSchedulingV1alpha1WorkloadForAllNamespaces", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.scheduling.v1alpha1.WorkloadList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "scheduling_v1alpha1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "scheduling.k8s.io", + "kind": "Workload", + "version": "v1alpha1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + } + } +} diff --git a/api/openapi-spec/v3/apis__storage.k8s.io__v1_openapi.json b/api/openapi-spec/v3/apis__storage.k8s.io__v1_openapi.json index 6369f7164d49d..6212f1d5363b1 100644 --- a/api/openapi-spec/v3/apis__storage.k8s.io__v1_openapi.json +++ b/api/openapi-spec/v3/apis__storage.k8s.io__v1_openapi.json @@ -800,7 +800,7 @@ "$ref": "#/components/schemas/io.k8s.api.core.v1.VolumeNodeAffinity" } ], - "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume." + "description": "nodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume. This field is mutable if MutablePVNodeAffinity feature gate is enabled." }, "persistentVolumeReclaimPolicy": { "description": "persistentVolumeReclaimPolicy defines what happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", @@ -1311,6 +1311,10 @@ "description": "seLinuxMount specifies if the CSI driver supports \"-o context\" mount option.\n\nWhen \"true\", the CSI driver must ensure that all volumes provided by this CSI driver can be mounted separately with different `-o context` options. This is typical for storage backends that provide volumes as filesystems on block devices or as independent shared volumes. Kubernetes will call NodeStage / NodePublish with \"-o context=xyz\" mount option when mounting a ReadWriteOncePod volume used in Pod that has explicitly set SELinux context. In the future, it may be expanded to other volume AccessModes. In any case, Kubernetes will ensure that the volume is mounted only with a single SELinux context.\n\nWhen \"false\", Kubernetes won't pass any special SELinux mount options to the driver. This is typical for volumes that represent subdirectories of a bigger shared filesystem.\n\nDefault is \"false\".", "type": "boolean" }, + "serviceAccountTokenInSecrets": { + "description": "serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that service account tokens should be passed via the Secrets field in NodePublishVolumeRequest instead of the VolumeContext field. The CSI specification provides a dedicated Secrets field for sensitive information like tokens, which is the appropriate mechanism for handling credentials. This addresses security concerns where sensitive tokens were being logged as part of volume context.\n\nWhen \"true\", kubelet will pass the tokens only in the Secrets field with the key \"csi.storage.k8s.io/serviceAccount.tokens\". The CSI driver must be updated to read tokens from the Secrets field instead of VolumeContext.\n\nWhen \"false\" or not set, kubelet will pass the tokens in VolumeContext with the key \"csi.storage.k8s.io/serviceAccount.tokens\" (existing behavior). This maintains backward compatibility with existing CSI drivers.\n\nThis field can only be set when TokenRequests is configured. The API server will reject CSIDriver specs that set this field without TokenRequests.\n\nDefault behavior if unset is to pass tokens in the VolumeContext field.", + "type": "boolean" + }, "storageCapacity": { "description": "storageCapacity indicates that the CSI volume driver wants pod scheduling to consider the storage capacity that the driver deployment will report by creating CSIStorageCapacity objects with capacity information, if set to true.\n\nThe check can be enabled immediately when deploying a driver. In that case, provisioning new volumes with late binding will pause until the driver deployment has published some suitable CSIStorageCapacity object.\n\nAlternatively, the driver can be deployed with the field unset or false and it can be flipped later when storage capacity information has been published.\n\nThis field was immutable in Kubernetes <= 1.22 and now is mutable.", "type": "boolean" @@ -2530,7 +2534,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -2850,8 +2854,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -3281,7 +3284,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__storage.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__storage.k8s.io__v1alpha1_openapi.json deleted file mode 100644 index 75f42f5b76585..0000000000000 --- a/api/openapi-spec/v3/apis__storage.k8s.io__v1alpha1_openapi.json +++ /dev/null @@ -1,2554 +0,0 @@ -{ - "components": { - "schemas": { - "io.k8s.api.storage.v1alpha1.VolumeAttributesClass": { - "description": "VolumeAttributesClass represents a specification of mutable volume attributes defined by the CSI driver. The class can be specified during dynamic provisioning of PersistentVolumeClaims, and changed in the PersistentVolumeClaim spec after provisioning.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "driverName": { - "default": "", - "description": "Name of the CSI driver This field is immutable.", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - } - ], - "default": {}, - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "parameters": { - "additionalProperties": { - "default": "", - "type": "string" - }, - "description": "parameters hold volume attributes defined by the CSI driver. These values are opaque to the Kubernetes and are passed directly to the CSI driver. The underlying storage provider supports changing these attributes on an existing volume, however the parameters field itself is immutable. To invoke a volume update, a new VolumeAttributesClass should be created with new parameters, and the PersistentVolumeClaim should be updated to reference the new VolumeAttributesClass.\n\nThis field is required and must contain at least one key/value pair. The keys cannot be empty, and the maximum number of parameters is 512, with a cumulative max size of 256K. If the CSI driver rejects invalid parameters, the target PersistentVolumeClaim will be set to an \"Infeasible\" state in the modifyVolumeStatus field.", - "type": "object" - } - }, - "required": [ - "driverName" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.storage.v1alpha1.VolumeAttributesClassList": { - "description": "VolumeAttributesClassList is a collection of VolumeAttributesClass objects.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "items is the list of VolumeAttributesClass objects.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - ], - "default": {} - }, - "type": "array" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ], - "default": {}, - "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClassList", - "version": "v1alpha1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { - "description": "APIResource specifies the name of a resource and whether it is namespaced.", - "properties": { - "categories": { - "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "group": { - "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", - "type": "string" - }, - "kind": { - "default": "", - "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", - "type": "string" - }, - "name": { - "default": "", - "description": "name is the plural name of the resource.", - "type": "string" - }, - "namespaced": { - "default": false, - "description": "namespaced indicates if a resource is namespaced or not.", - "type": "boolean" - }, - "shortNames": { - "description": "shortNames is a list of suggested short names of the resource.", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "singularName": { - "default": "", - "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", - "type": "string" - }, - "storageVersionHash": { - "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", - "type": "string" - }, - "verbs": { - "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", - "items": { - "default": "", - "type": "string" - }, - "type": "array" - }, - "version": { - "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", - "type": "string" - } - }, - "required": [ - "name", - "singularName", - "namespaced", - "kind", - "verbs" - ], - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { - "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "groupVersion": { - "default": "", - "description": "groupVersion is the group and version this APIResourceList is for.", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "resources": { - "description": "resources contains the name of the resources and if they are namespaced.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - } - }, - "required": [ - "groupVersion", - "resources" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "APIResourceList", - "version": "v1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { - "description": "DeleteOptions may be provided when deleting an API object.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "dryRun": { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "gracePeriodSeconds": { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "format": "int64", - "type": "integer" - }, - "ignoreStoreReadErrorWithClusterBreakingPotential": { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "type": "boolean" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "orphanDependents": { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "type": "boolean" - }, - "preconditions": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" - } - ], - "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." - }, - "propagationPolicy": { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "type": "string" - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha2" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta3" - }, - { - "group": "imagepolicy.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha3" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { - "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { - "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", - "properties": { - "continue": { - "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", - "type": "string" - }, - "remainingItemCount": { - "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", - "format": "int64", - "type": "integer" - }, - "resourceVersion": { - "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - "type": "string" - }, - "selfLink": { - "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { - "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", - "type": "string" - }, - "fieldsType": { - "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", - "type": "string" - }, - "fieldsV1": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" - } - ], - "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type." - }, - "manager": { - "description": "Manager is an identifier of the workflow managing these fields.", - "type": "string" - }, - "operation": { - "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", - "type": "string" - }, - "subresource": { - "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", - "type": "string" - }, - "time": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over." - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { - "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", - "properties": { - "annotations": { - "additionalProperties": { - "default": "", - "type": "string" - }, - "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", - "type": "object" - }, - "creationTimestamp": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "deletionGracePeriodSeconds": { - "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", - "format": "int64", - "type": "integer" - }, - "deletionTimestamp": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "finalizers": { - "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "set", - "x-kubernetes-patch-strategy": "merge" - }, - "generateName": { - "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", - "type": "string" - }, - "generation": { - "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", - "format": "int64", - "type": "integer" - }, - "labels": { - "additionalProperties": { - "default": "", - "type": "string" - }, - "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", - "type": "object" - }, - "managedFields": { - "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "name": { - "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", - "type": "string" - }, - "namespace": { - "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", - "type": "string" - }, - "ownerReferences": { - "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "uid" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "uid", - "x-kubernetes-patch-strategy": "merge" - }, - "resourceVersion": { - "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - "type": "string" - }, - "selfLink": { - "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", - "type": "string" - }, - "uid": { - "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { - "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", - "properties": { - "apiVersion": { - "default": "", - "description": "API version of the referent.", - "type": "string" - }, - "blockOwnerDeletion": { - "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", - "type": "boolean" - }, - "controller": { - "description": "If true, this reference points to the managing controller.", - "type": "boolean" - }, - "kind": { - "default": "", - "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "name": { - "default": "", - "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", - "type": "string" - }, - "uid": { - "default": "", - "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "required": [ - "apiVersion", - "kind", - "name", - "uid" - ], - "type": "object", - "x-kubernetes-map-type": "atomic" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { - "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { - "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", - "properties": { - "resourceVersion": { - "description": "Specifies the target ResourceVersion", - "type": "string" - }, - "uid": { - "description": "Specifies the target UID.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { - "description": "Status is a return value for calls that don't return other objects.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "code": { - "description": "Suggested HTTP return code for this status, 0 if not set.", - "format": "int32", - "type": "integer" - }, - "details": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" - } - ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "message": { - "description": "A human-readable description of the status of this operation.", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ], - "default": {}, - "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" - }, - "reason": { - "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", - "type": "string" - }, - "status": { - "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - "type": "string" - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "Status", - "version": "v1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { - "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", - "properties": { - "field": { - "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", - "type": "string" - }, - "message": { - "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", - "type": "string" - }, - "reason": { - "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { - "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", - "properties": { - "causes": { - "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "group": { - "description": "The group attribute of the resource associated with the status StatusReason.", - "type": "string" - }, - "kind": { - "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "name": { - "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", - "type": "string" - }, - "retryAfterSeconds": { - "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", - "format": "int32", - "type": "integer" - }, - "uid": { - "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { - "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", - "format": "date-time", - "type": "string" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { - "description": "Event represents a single event to a watched resource.", - "properties": { - "object": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension" - } - ], - "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." - }, - "type": { - "default": "", - "type": "string" - } - }, - "required": [ - "type", - "object" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha2" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta3" - }, - { - "group": "imagepolicy.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha3" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - } - ] - }, - "io.k8s.apimachinery.pkg.runtime.RawExtension": { - "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", - "type": "object" - } - }, - "securitySchemes": { - "BearerToken": { - "description": "Bearer Token authentication", - "in": "header", - "name": "authorization", - "type": "apiKey" - } - } - }, - "info": { - "title": "Kubernetes", - "version": "unversioned" - }, - "openapi": "3.0.0", - "paths": { - "/apis/storage.k8s.io/v1alpha1/": { - "get": { - "description": "get available resources", - "operationId": "getStorageV1alpha1APIResources", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ] - } - }, - "/apis/storage.k8s.io/v1alpha1/volumeattributesclasses": { - "delete": { - "description": "delete collection of VolumeAttributesClass", - "operationId": "deleteStorageV1alpha1CollectionVolumeAttributesClass", - "parameters": [ - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "deletecollection", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "get": { - "description": "list or watch objects of kind VolumeAttributesClass", - "operationId": "listStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClassList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "list", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "post": { - "description": "create a VolumeAttributesClass", - "operationId": "createStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "Created" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "post", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - } - }, - "/apis/storage.k8s.io/v1alpha1/volumeattributesclasses/{name}": { - "delete": { - "description": "delete a VolumeAttributesClass", - "operationId": "deleteStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "OK" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "delete", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "get": { - "description": "read the specified VolumeAttributesClass", - "operationId": "readStorageV1alpha1VolumeAttributesClass", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "get", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "name of the VolumeAttributesClass", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "patch": { - "description": "partially update the specified VolumeAttributesClass", - "operationId": "patchStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - "in": "query", - "name": "force", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "application/apply-patch+cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/apply-patch+yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/strategic-merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "put": { - "description": "replace the specified VolumeAttributesClass", - "operationId": "replaceStorageV1alpha1VolumeAttributesClass", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storage.v1alpha1.VolumeAttributesClass" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - } - }, - "/apis/storage.k8s.io/v1alpha1/watch/volumeattributesclasses": { - "get": { - "description": "watch individual changes to a list of VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStorageV1alpha1VolumeAttributesClassList", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/storage.k8s.io/v1alpha1/watch/volumeattributesclasses/{name}": { - "get": { - "description": "watch changes to an object of kind VolumeAttributesClass. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStorageV1alpha1VolumeAttributesClass", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storage_v1alpha1" - ], - "x-kubernetes-action": "watch", - "x-kubernetes-group-version-kind": { - "group": "storage.k8s.io", - "kind": "VolumeAttributesClass", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "name of the VolumeAttributesClass", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - } - } -} diff --git a/api/openapi-spec/v3/apis__storage.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__storage.k8s.io__v1beta1_openapi.json index d785e010cb018..0843bd9e9e178 100644 --- a/api/openapi-spec/v3/apis__storage.k8s.io__v1beta1_openapi.json +++ b/api/openapi-spec/v3/apis__storage.k8s.io__v1beta1_openapi.json @@ -562,7 +562,7 @@ { "group": "storagemigration.k8s.io", "kind": "DeleteOptions", - "version": "v1alpha1" + "version": "v1beta1" } ] }, @@ -825,8 +825,7 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" } ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", @@ -1256,7 +1255,7 @@ { "group": "storagemigration.k8s.io", "kind": "WatchEvent", - "version": "v1alpha1" + "version": "v1beta1" } ] }, diff --git a/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1alpha1_openapi.json deleted file mode 100644 index 33767db9a4712..0000000000000 --- a/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1alpha1_openapi.json +++ /dev/null @@ -1,2975 +0,0 @@ -{ - "components": { - "schemas": { - "io.k8s.api.storagemigration.v1alpha1.GroupVersionResource": { - "description": "The names of the group, the version, and the resource.", - "properties": { - "group": { - "description": "The name of the group.", - "type": "string" - }, - "resource": { - "description": "The name of the resource.", - "type": "string" - }, - "version": { - "description": "The name of the version.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.api.storagemigration.v1alpha1.MigrationCondition": { - "description": "Describes the state of a migration at a certain point.", - "properties": { - "lastUpdateTime": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "The last time this condition was updated." - }, - "message": { - "description": "A human readable message indicating details about the transition.", - "type": "string" - }, - "reason": { - "description": "The reason for the condition's last transition.", - "type": "string" - }, - "status": { - "default": "", - "description": "Status of the condition, one of True, False, Unknown.", - "type": "string" - }, - "type": { - "default": "", - "description": "Type of the condition.", - "type": "string" - } - }, - "required": [ - "type", - "status" - ], - "type": "object" - }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration": { - "description": "StorageVersionMigration represents a migration of stored data to the latest storage version.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - } - ], - "default": {}, - "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "spec": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationSpec" - } - ], - "default": {}, - "description": "Specification of the migration." - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationStatus" - } - ], - "default": {}, - "description": "Status of the migration." - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList": { - "description": "StorageVersionMigrationList is a collection of storage version migrations.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "Items is the list of StorageVersionMigration", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "type" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ], - "default": {}, - "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigrationList", - "version": "v1alpha1" - } - ] - }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationSpec": { - "description": "Spec of the storage version migration.", - "properties": { - "continueToken": { - "description": "The token used in the list options to get the next chunk of objects to migrate. When the .status.conditions indicates the migration is \"Running\", users can use this token to check the progress of the migration.", - "type": "string" - }, - "resource": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.GroupVersionResource" - } - ], - "default": {}, - "description": "The resource that is being migrated. The migrator sends requests to the endpoint serving the resource. Immutable." - } - }, - "required": [ - "resource" - ], - "type": "object" - }, - "io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationStatus": { - "description": "Status of the storage version migration.", - "properties": { - "conditions": { - "description": "The latest available observations of the migration's current state.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.MigrationCondition" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "type" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge" - }, - "resourceVersion": { - "description": "ResourceVersion to compare with the GC cache for performing the migration. This is the current resource version of given group, version and resource when kube-controller-manager first observes this StorageVersionMigration resource.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { - "description": "APIResource specifies the name of a resource and whether it is namespaced.", - "properties": { - "categories": { - "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "group": { - "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", - "type": "string" - }, - "kind": { - "default": "", - "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", - "type": "string" - }, - "name": { - "default": "", - "description": "name is the plural name of the resource.", - "type": "string" - }, - "namespaced": { - "default": false, - "description": "namespaced indicates if a resource is namespaced or not.", - "type": "boolean" - }, - "shortNames": { - "description": "shortNames is a list of suggested short names of the resource.", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "singularName": { - "default": "", - "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", - "type": "string" - }, - "storageVersionHash": { - "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", - "type": "string" - }, - "verbs": { - "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", - "items": { - "default": "", - "type": "string" - }, - "type": "array" - }, - "version": { - "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", - "type": "string" - } - }, - "required": [ - "name", - "singularName", - "namespaced", - "kind", - "verbs" - ], - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { - "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "groupVersion": { - "default": "", - "description": "groupVersion is the group and version this APIResourceList is for.", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "resources": { - "description": "resources contains the name of the resources and if they are namespaced.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - } - }, - "required": [ - "groupVersion", - "resources" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "APIResourceList", - "version": "v1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { - "description": "DeleteOptions may be provided when deleting an API object.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "dryRun": { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "gracePeriodSeconds": { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "format": "int64", - "type": "integer" - }, - "ignoreStoreReadErrorWithClusterBreakingPotential": { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "type": "boolean" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "orphanDependents": { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "type": "boolean" - }, - "preconditions": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" - } - ], - "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." - }, - "propagationPolicy": { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "type": "string" - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "DeleteOptions", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "batch", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha2" - }, - { - "group": "coordination.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta3" - }, - { - "group": "imagepolicy.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "policy", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha3" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta2" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "DeleteOptions", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "DeleteOptions", - "version": "v1alpha1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { - "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { - "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", - "properties": { - "continue": { - "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", - "type": "string" - }, - "remainingItemCount": { - "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", - "format": "int64", - "type": "integer" - }, - "resourceVersion": { - "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - "type": "string" - }, - "selfLink": { - "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { - "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", - "type": "string" - }, - "fieldsType": { - "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", - "type": "string" - }, - "fieldsV1": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" - } - ], - "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type." - }, - "manager": { - "description": "Manager is an identifier of the workflow managing these fields.", - "type": "string" - }, - "operation": { - "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", - "type": "string" - }, - "subresource": { - "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", - "type": "string" - }, - "time": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over." - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { - "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", - "properties": { - "annotations": { - "additionalProperties": { - "default": "", - "type": "string" - }, - "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", - "type": "object" - }, - "creationTimestamp": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "deletionGracePeriodSeconds": { - "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", - "format": "int64", - "type": "integer" - }, - "deletionTimestamp": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ], - "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" - }, - "finalizers": { - "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", - "items": { - "default": "", - "type": "string" - }, - "type": "array", - "x-kubernetes-list-type": "set", - "x-kubernetes-patch-strategy": "merge" - }, - "generateName": { - "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", - "type": "string" - }, - "generation": { - "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", - "format": "int64", - "type": "integer" - }, - "labels": { - "additionalProperties": { - "default": "", - "type": "string" - }, - "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", - "type": "object" - }, - "managedFields": { - "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "name": { - "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", - "type": "string" - }, - "namespace": { - "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", - "type": "string" - }, - "ownerReferences": { - "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-map-keys": [ - "uid" - ], - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "uid", - "x-kubernetes-patch-strategy": "merge" - }, - "resourceVersion": { - "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - "type": "string" - }, - "selfLink": { - "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", - "type": "string" - }, - "uid": { - "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { - "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", - "properties": { - "apiVersion": { - "default": "", - "description": "API version of the referent.", - "type": "string" - }, - "blockOwnerDeletion": { - "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", - "type": "boolean" - }, - "controller": { - "description": "If true, this reference points to the managing controller.", - "type": "boolean" - }, - "kind": { - "default": "", - "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "name": { - "default": "", - "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", - "type": "string" - }, - "uid": { - "default": "", - "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "required": [ - "apiVersion", - "kind", - "name", - "uid" - ], - "type": "object", - "x-kubernetes-map-type": "atomic" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { - "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { - "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", - "properties": { - "resourceVersion": { - "description": "Specifies the target ResourceVersion", - "type": "string" - }, - "uid": { - "description": "Specifies the target UID.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { - "description": "Status is a return value for calls that don't return other objects.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "code": { - "description": "Suggested HTTP return code for this status, 0 if not set.", - "format": "int32", - "type": "integer" - }, - "details": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" - } - ], - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "x-kubernetes-list-type": "atomic" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "message": { - "description": "A human-readable description of the status of this operation.", - "type": "string" - }, - "metadata": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ], - "default": {}, - "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" - }, - "reason": { - "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", - "type": "string" - }, - "status": { - "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - "type": "string" - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "Status", - "version": "v1" - } - ] - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { - "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", - "properties": { - "field": { - "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", - "type": "string" - }, - "message": { - "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", - "type": "string" - }, - "reason": { - "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { - "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", - "properties": { - "causes": { - "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" - } - ], - "default": {} - }, - "type": "array", - "x-kubernetes-list-type": "atomic" - }, - "group": { - "description": "The group attribute of the resource associated with the status StatusReason.", - "type": "string" - }, - "kind": { - "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "name": { - "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", - "type": "string" - }, - "retryAfterSeconds": { - "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", - "format": "int32", - "type": "integer" - }, - "uid": { - "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", - "type": "string" - } - }, - "type": "object" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { - "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", - "format": "date-time", - "type": "string" - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { - "description": "Event represents a single event to a watched resource.", - "properties": { - "object": { - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension" - } - ], - "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." - }, - "type": { - "default": "", - "type": "string" - } - }, - "required": [ - "type", - "object" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admission.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "admissionregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apiextensions.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apiregistration.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "apps", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "authentication.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta1" - }, - { - "group": "autoscaling", - "kind": "WatchEvent", - "version": "v2beta2" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "batch", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "certificates.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha2" - }, - { - "group": "coordination.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "discovery.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "events.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "extensions", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "flowcontrol.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1beta3" - }, - { - "group": "imagepolicy.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "internal.apiserver.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "networking.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "node.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "policy", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "rbac.authorization.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha3" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "resource.k8s.io", - "kind": "WatchEvent", - "version": "v1beta2" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "scheduling.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - }, - { - "group": "storage.k8s.io", - "kind": "WatchEvent", - "version": "v1beta1" - }, - { - "group": "storagemigration.k8s.io", - "kind": "WatchEvent", - "version": "v1alpha1" - } - ] - }, - "io.k8s.apimachinery.pkg.runtime.RawExtension": { - "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", - "type": "object" - } - }, - "securitySchemes": { - "BearerToken": { - "description": "Bearer Token authentication", - "in": "header", - "name": "authorization", - "type": "apiKey" - } - } - }, - "info": { - "title": "Kubernetes", - "version": "unversioned" - }, - "openapi": "3.0.0", - "paths": { - "/apis/storagemigration.k8s.io/v1alpha1/": { - "get": { - "description": "get available resources", - "operationId": "getStoragemigrationV1alpha1APIResources", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ] - } - }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations": { - "delete": { - "description": "delete collection of StorageVersionMigration", - "operationId": "deleteStoragemigrationV1alpha1CollectionStorageVersionMigration", - "parameters": [ - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "deletecollection", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "get": { - "description": "list or watch objects of kind StorageVersionMigration", - "operationId": "listStoragemigrationV1alpha1StorageVersionMigration", - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigrationList" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "list", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "post": { - "description": "create a StorageVersionMigration", - "operationId": "createStoragemigrationV1alpha1StorageVersionMigration", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Created" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "post", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - } - }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations/{name}": { - "delete": { - "description": "delete a StorageVersionMigration", - "operationId": "deleteStoragemigrationV1alpha1StorageVersionMigration", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - "in": "query", - "name": "gracePeriodSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", - "in": "query", - "name": "ignoreStoreReadErrorWithClusterBreakingPotential", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - "in": "query", - "name": "orphanDependents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - "in": "query", - "name": "propagationPolicy", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - } - } - } - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "OK" - }, - "202": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - } - } - }, - "description": "Accepted" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "delete", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "get": { - "description": "read the specified StorageVersionMigration", - "operationId": "readStoragemigrationV1alpha1StorageVersionMigration", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "get", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "name of the StorageVersionMigration", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "patch": { - "description": "partially update the specified StorageVersionMigration", - "operationId": "patchStoragemigrationV1alpha1StorageVersionMigration", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - "in": "query", - "name": "force", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "application/apply-patch+cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/apply-patch+yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/strategic-merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "put": { - "description": "replace the specified StorageVersionMigration", - "operationId": "replaceStoragemigrationV1alpha1StorageVersionMigration", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - } - }, - "/apis/storagemigration.k8s.io/v1alpha1/storageversionmigrations/{name}/status": { - "get": { - "description": "read status of the specified StorageVersionMigration", - "operationId": "readStoragemigrationV1alpha1StorageVersionMigrationStatus", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "get", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "name of the StorageVersionMigration", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "patch": { - "description": "partially update status of the specified StorageVersionMigration", - "operationId": "patchStoragemigrationV1alpha1StorageVersionMigrationStatus", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - "in": "query", - "name": "force", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "application/apply-patch+cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/apply-patch+yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/json-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - }, - "application/strategic-merge-patch+json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "patch", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "put": { - "description": "replace status of the specified StorageVersionMigration", - "operationId": "replaceStoragemigrationV1alpha1StorageVersionMigrationStatus", - "parameters": [ - { - "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - "in": "query", - "name": "dryRun", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - "in": "query", - "name": "fieldManager", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", - "in": "query", - "name": "fieldValidation", - "schema": { - "type": "string", - "uniqueItems": true - } - } - ], - "requestBody": { - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "OK" - }, - "201": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1alpha1.StorageVersionMigration" - } - } - }, - "description": "Created" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "put", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - } - }, - "/apis/storagemigration.k8s.io/v1alpha1/watch/storageversionmigrations": { - "get": { - "description": "watch individual changes to a list of StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead.", - "operationId": "watchStoragemigrationV1alpha1StorageVersionMigrationList", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - }, - "/apis/storagemigration.k8s.io/v1alpha1/watch/storageversionmigrations/{name}": { - "get": { - "description": "watch changes to an object of kind StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "operationId": "watchStoragemigrationV1alpha1StorageVersionMigration", - "responses": { - "200": { - "content": { - "application/cbor": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/cbor-seq": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/json;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/vnd.kubernetes.protobuf;stream=watch": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - }, - "application/yaml": { - "schema": { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "description": "OK" - }, - "401": { - "description": "Unauthorized" - } - }, - "tags": [ - "storagemigration_v1alpha1" - ], - "x-kubernetes-action": "watch", - "x-kubernetes-group-version-kind": { - "group": "storagemigration.k8s.io", - "kind": "StorageVersionMigration", - "version": "v1alpha1" - } - }, - "parameters": [ - { - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", - "in": "query", - "name": "allowWatchBookmarks", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "in": "query", - "name": "continue", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "in": "query", - "name": "fieldSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "in": "query", - "name": "labelSelector", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "in": "query", - "name": "limit", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "name of the StorageVersionMigration", - "in": "path", - "name": "name", - "required": true, - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", - "in": "query", - "name": "pretty", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersion", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", - "in": "query", - "name": "resourceVersionMatch", - "schema": { - "type": "string", - "uniqueItems": true - } - }, - { - "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", - "in": "query", - "name": "sendInitialEvents", - "schema": { - "type": "boolean", - "uniqueItems": true - } - }, - { - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "in": "query", - "name": "timeoutSeconds", - "schema": { - "type": "integer", - "uniqueItems": true - } - }, - { - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "in": "query", - "name": "watch", - "schema": { - "type": "boolean", - "uniqueItems": true - } - } - ] - } - } -} diff --git a/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1beta1_openapi.json b/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1beta1_openapi.json new file mode 100644 index 0000000000000..537f1baccb5be --- /dev/null +++ b/api/openapi-spec/v3/apis__storagemigration.k8s.io__v1beta1_openapi.json @@ -0,0 +1,2974 @@ +{ + "components": { + "schemas": { + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigration": { + "description": "StorageVersionMigration represents a migration of stored data to the latest storage version.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ], + "default": {}, + "description": "Standard object metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "spec": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationSpec" + } + ], + "default": {}, + "description": "Specification of the migration." + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationStatus" + } + ], + "default": {}, + "description": "Status of the migration." + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList": { + "description": "StorageVersionMigrationList is a collection of storage version migrations.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "Items is the list of StorageVersionMigration", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + ], + "default": {} + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ], + "default": {}, + "description": "Standard list metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigrationList", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationSpec": { + "description": "Spec of the storage version migration.", + "properties": { + "resource": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.GroupResource" + } + ], + "default": {}, + "description": "The resource that is being migrated. The migrator sends requests to the endpoint serving the resource. Immutable." + } + }, + "required": [ + "resource" + ], + "type": "object" + }, + "io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationStatus": { + "description": "Status of the storage version migration.", + "properties": { + "conditions": { + "description": "The latest available observations of the migration's current state.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "ResourceVersion to compare with the GC cache for performing the migration. This is the current resource version of given group, version and resource when kube-controller-manager first observes this StorageVersionMigration resource.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + "type": "string" + }, + "kind": { + "default": "", + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string" + }, + "name": { + "default": "", + "description": "name is the plural name of the resource.", + "type": "string" + }, + "namespaced": { + "default": false, + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean" + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "singularName": { + "default": "", + "description": "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + "type": "string" + }, + "storageVersionHash": { + "description": "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "items": { + "default": "", + "type": "string" + }, + "type": "array" + }, + "version": { + "description": "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + "type": "string" + } + }, + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "groupVersion": { + "default": "", + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "groupVersion", + "resources" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "APIResourceList", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Condition": { + "description": "Condition contains details for one aspect of the current state of this API Resource.", + "properties": { + "lastTransitionTime": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable." + }, + "message": { + "default": "", + "description": "message is a human readable message indicating details about the transition. This may be an empty string.", + "type": "string" + }, + "observedGeneration": { + "description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.", + "format": "int64", + "type": "integer" + }, + "reason": { + "default": "", + "description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.", + "type": "string" + }, + "status": { + "default": "", + "description": "status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "default": "", + "description": "type of condition in CamelCase or in foo.example.com/CamelCase.", + "type": "string" + } + }, + "required": [ + "type", + "status", + "lastTransitionTime", + "reason", + "message" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "dryRun": { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "gracePeriodSeconds": { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "format": "int64", + "type": "integer" + }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "type": "boolean" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "orphanDependents": { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "type": "boolean" + }, + "preconditions": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" + } + ], + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned." + }, + "propagationPolicy": { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "DeleteOptions", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "batch", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha2" + }, + { + "group": "coordination.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "policy", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha3" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta2" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + }, + { + "group": "storagemigration.k8s.io", + "kind": "DeleteOptions", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": { + "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.GroupResource": { + "description": "GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + "properties": { + "group": { + "default": "", + "type": "string" + }, + "resource": { + "default": "", + "type": "string" + } + }, + "required": [ + "group", + "resource" + ], + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "properties": { + "continue": { + "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + "type": "string" + }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "format": "int64", + "type": "integer" + }, + "resourceVersion": { + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" + } + ], + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type." + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", + "type": "string" + }, + "time": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over." + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "properties": { + "annotations": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations", + "type": "object" + }, + "creationTimestamp": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "deletionGracePeriodSeconds": { + "description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + "format": "int64", + "type": "integer" + }, + "deletionTimestamp": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ], + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata" + }, + "finalizers": { + "description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.", + "items": { + "default": "", + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "set", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "format": "int64", + "type": "integer" + }, + "labels": { + "additionalProperties": { + "default": "", + "type": "string" + }, + "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels", + "type": "object" + }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "name": { + "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "namespace": { + "description": "Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "properties": { + "apiVersion": { + "default": "", + "description": "API version of the referent.", + "type": "string" + }, + "blockOwnerDeletion": { + "description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "default": "", + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "default": "", + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string" + }, + "uid": { + "default": "", + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "format": "int32", + "type": "integer" + }, + "details": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" + } + ], + "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type." + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ], + "default": {}, + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + }, + "reason": { + "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + "type": "string" + }, + "status": { + "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + "type": "string" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "Status", + "version": "v1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "properties": { + "field": { + "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + } + ], + "default": {} + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + "format": "int32", + "type": "integer" + }, + "uid": { + "description": "UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + }, + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "format": "date-time", + "type": "string" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent": { + "description": "Event represents a single event to a watched resource.", + "properties": { + "object": { + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension" + } + ], + "description": "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context." + }, + "type": { + "default": "", + "type": "string" + } + }, + "required": [ + "type", + "object" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admission.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "admissionregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiextensions.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apiregistration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "apps", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "authentication.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta1" + }, + { + "group": "autoscaling", + "kind": "WatchEvent", + "version": "v2beta2" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "batch", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "certificates.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha2" + }, + { + "group": "coordination.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "discovery.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "events.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "extensions", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "flowcontrol.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1beta3" + }, + { + "group": "imagepolicy.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "internal.apiserver.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "networking.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "node.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "policy", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "rbac.authorization.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha3" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "resource.k8s.io", + "kind": "WatchEvent", + "version": "v1beta2" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "scheduling.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, + { + "group": "storage.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + }, + { + "group": "storagemigration.k8s.io", + "kind": "WatchEvent", + "version": "v1beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.runtime.RawExtension": { + "description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + "type": "object" + } + }, + "securitySchemes": { + "BearerToken": { + "description": "Bearer Token authentication", + "in": "header", + "name": "authorization", + "type": "apiKey" + } + } + }, + "info": { + "title": "Kubernetes", + "version": "unversioned" + }, + "openapi": "3.0.0", + "paths": { + "/apis/storagemigration.k8s.io/v1beta1/": { + "get": { + "description": "get available resources", + "operationId": "getStoragemigrationV1beta1APIResources", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ] + } + }, + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations": { + "delete": { + "description": "delete collection of StorageVersionMigration", + "operationId": "deleteStoragemigrationV1beta1CollectionStorageVersionMigration", + "parameters": [ + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "get": { + "description": "list or watch objects of kind StorageVersionMigration", + "operationId": "listStoragemigrationV1beta1StorageVersionMigration", + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigrationList" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "post": { + "description": "create a StorageVersionMigration", + "operationId": "createStoragemigrationV1beta1StorageVersionMigration", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Created" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + } + }, + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations/{name}": { + "delete": { + "description": "delete a StorageVersionMigration", + "operationId": "deleteStoragemigrationV1beta1StorageVersionMigration", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "in": "query", + "name": "gracePeriodSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + "in": "query", + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "in": "query", + "name": "orphanDependents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "in": "query", + "name": "propagationPolicy", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "OK" + }, + "202": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "description": "Accepted" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "get": { + "description": "read the specified StorageVersionMigration", + "operationId": "readStoragemigrationV1beta1StorageVersionMigration", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "name of the StorageVersionMigration", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update the specified StorageVersionMigration", + "operationId": "patchStoragemigrationV1beta1StorageVersionMigration", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "put": { + "description": "replace the specified StorageVersionMigration", + "operationId": "replaceStoragemigrationV1beta1StorageVersionMigration", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + } + }, + "/apis/storagemigration.k8s.io/v1beta1/storageversionmigrations/{name}/status": { + "get": { + "description": "read status of the specified StorageVersionMigration", + "operationId": "readStoragemigrationV1beta1StorageVersionMigrationStatus", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "name of the StorageVersionMigration", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "patch": { + "description": "partially update status of the specified StorageVersionMigration", + "operationId": "patchStoragemigrationV1beta1StorageVersionMigrationStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "in": "query", + "name": "force", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "put": { + "description": "replace status of the specified StorageVersionMigration", + "operationId": "replaceStoragemigrationV1beta1StorageVersionMigrationStatus", + "parameters": [ + { + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "in": "query", + "name": "dryRun", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "in": "query", + "name": "fieldManager", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.", + "in": "query", + "name": "fieldValidation", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "OK" + }, + "201": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.api.storagemigration.v1beta1.StorageVersionMigration" + } + } + }, + "description": "Created" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + } + }, + "/apis/storagemigration.k8s.io/v1beta1/watch/storageversionmigrations": { + "get": { + "description": "watch individual changes to a list of StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead.", + "operationId": "watchStoragemigrationV1beta1StorageVersionMigrationList", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + }, + "/apis/storagemigration.k8s.io/v1beta1/watch/storageversionmigrations/{name}": { + "get": { + "description": "watch changes to an object of kind StorageVersionMigration. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "operationId": "watchStoragemigrationV1beta1StorageVersionMigration", + "responses": { + "200": { + "content": { + "application/cbor": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/cbor-seq": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "tags": [ + "storagemigration_v1beta1" + ], + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "storagemigration.k8s.io", + "kind": "StorageVersionMigration", + "version": "v1beta1" + } + }, + "parameters": [ + { + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", + "in": "query", + "name": "allowWatchBookmarks", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "in": "query", + "name": "continue", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "in": "query", + "name": "fieldSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "in": "query", + "name": "labelSelector", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "name of the StorageVersionMigration", + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).", + "in": "query", + "name": "pretty", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersion", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", + "in": "query", + "name": "resourceVersionMatch", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "description": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "in": "query", + "name": "sendInitialEvents", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "in": "query", + "name": "timeoutSeconds", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "in": "query", + "name": "watch", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ] + } + } +} diff --git a/build/README.md b/build/README.md index 3efb2f869519d..f0fd3adf78e84 100644 --- a/build/README.md +++ b/build/README.md @@ -46,19 +46,14 @@ The following scripts are found in the [`build/`](.) directory. * `build/run.sh make test`: Run all unit tests * `build/run.sh make test-integration`: Run integration test * `build/run.sh make test-cmd`: Run CLI tests -* [`build/copy-output.sh`](copy-output.sh): This will copy the contents of `_output/dockerized/bin` from the Docker container to the local `_output/dockerized/bin`. It will also copy out specific file patterns that are generated as part of the build process. This is run automatically as part of `build/run.sh`. * [`build/make-clean.sh`](make-clean.sh): Clean out the contents of `_output`, remove any locally built container images and remove the data container. * [`build/shell.sh`](shell.sh): Drop into a `bash` shell in a build container with a snapshot of the current repo code. ## Basic Flow -The scripts directly under [`build/`](.) are used to build and test. They will ensure that the `kube-build` Docker image is built (based on [`build/build-image/Dockerfile`](build-image/Dockerfile) and after base image's `KUBE_BUILD_IMAGE_CROSS_TAG` from Dockerfile is replaced with one of those actual tags of the base image, like `v1.13.9-2`) and then execute the appropriate command in that container. These scripts will both ensure that the right data is cached from run to run for incremental builds and will copy the results back out of the container. You can specify a different registry/name and version for `kube-cross` by setting `KUBE_CROSS_IMAGE` and `KUBE_CROSS_VERSION`, see [`common.sh`](common.sh) for more details. - -The `kube-build` container image is built by first creating a "context" directory in `_output/images/build-image`. It is done there instead of at the root of the Kubernetes repo to minimize the amount of data we need to package up when building the image. - -There are 3 different containers instances that are run from this image. The first is a "data" container to store all data that needs to persist across to support incremental builds. Next there is an "rsync" container that is used to transfer data in and out to the data container. Lastly there is a "build" container that is used for actually doing build actions. The data container persists across runs while the rsync and build containers are deleted after each use. - -`rsync` is used transparently behind the scenes to efficiently move data in and out of the container. This will use an ephemeral port picked by Docker. You can modify this by setting the `KUBE_RSYNC_PORT` env variable. +The scripts directly under [`build/`](.) are used to build and test. +They will build using docker containers based on the `kube-cross` from https://git.k8s.io/release/tree/master/images/build/cross. +You can specify a different registry/name and version for `kube-cross` by setting `KUBE_CROSS_IMAGE` and `KUBE_CROSS_VERSION`, see [`common.sh`](common.sh) for more details. All Docker names are suffixed with a hash derived from the file path (to allow concurrent usage on things like CI machines) and a version number. When the version number changes all state is cleared and clean build is started. This allows the build infrastructure to be changed and signal to CI systems that old artifacts need to be deleted. diff --git a/build/build-image/Dockerfile b/build/build-image/Dockerfile deleted file mode 100644 index 4473935769b76..0000000000000 --- a/build/build-image/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2016 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. - -# This file creates a standard build environment for building Kubernetes -ARG KUBE_CROSS_IMAGE -ARG KUBE_CROSS_VERSION - -FROM ${KUBE_CROSS_IMAGE}:${KUBE_CROSS_VERSION} - -# Mark this as a kube-build container -RUN touch /kube-build-image - -# To run as non-root we sometimes need to rebuild go stdlib packages. -RUN chmod -R a+rwx /usr/local/go/pkg - -# For running integration tests /var/run/kubernetes is required -# and should be writable by user -RUN mkdir /var/run/kubernetes && chmod a+rwx /var/run/kubernetes - -# The kubernetes source is expected to be mounted here. This will be the base -# of operations. -ENV HOME=/go/src/k8s.io/kubernetes -WORKDIR ${HOME} - -# Make output from the dockerized build go someplace else -ENV KUBE_OUTPUT_SUBPATH=_output/dockerized - -# Pick up version stuff here as we don't copy our .git over. -ENV KUBE_GIT_VERSION_FILE=${HOME}/.dockerized-kube-version-defs - -# Add system-wide git user information -RUN git config --system user.email "nobody@k8s.io" \ - && git config --system user.name "kube-build-image" - -# Fix permissions on gopath -RUN chmod -R a+rwx $GOPATH - -# Make log messages use the right timezone -ADD localtime /etc/localtime -RUN chmod a+r /etc/localtime - -# Set up rsyncd -ADD rsyncd.password / -RUN chmod a+r /rsyncd.password -ADD rsyncd.sh / -RUN chmod a+rx /rsyncd.sh diff --git a/build/build-image/VERSION b/build/build-image/VERSION deleted file mode 100644 index 7ed6ff82de6bc..0000000000000 --- a/build/build-image/VERSION +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/build/build-image/cross/VERSION b/build/build-image/cross/VERSION index 14a4b84c2030d..43aec8a00f487 100644 --- a/build/build-image/cross/VERSION +++ b/build/build-image/cross/VERSION @@ -1 +1 @@ -v1.34.0-go1.24.9-bullseye.0 +v1.35.0-go1.25.6-bullseye.0 \ No newline at end of file diff --git a/build/build-image/rsyncd.sh b/build/build-image/rsyncd.sh deleted file mode 100755 index 2801fa6a38739..0000000000000 --- a/build/build-image/rsyncd.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 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. - -# This script will set up and run rsyncd to allow data to move into and out of -# our dockerized build system. This is used for syncing sources and changes of -# sources into the docker-build-container. It is also used to transfer built binaries -# and generated files back out. -# -# When run as root (rare) it'll preserve the file ids as sent from the client. -# Usually it'll be run as non-dockerized UID/GID and end up translating all file -# ownership to that. - - -set -o errexit -set -o nounset -set -o pipefail - -# The directory that gets sync'd -VOLUME=${HOME} - -# Assume that this is running in Docker on a bridge. Allow connections from -# anything on the local subnet. -ALLOW=$(ip route | awk '/^default via/ { reg = "^[0-9./]+ dev "$5 } ; $0 ~ reg { print $1 }') - -CONFDIR="/tmp/rsync.k8s" -PIDFILE="${CONFDIR}/rsyncd.pid" -CONFFILE="${CONFDIR}/rsyncd.conf" -SECRETS="${CONFDIR}/rsyncd.secrets" - -mkdir -p "${CONFDIR}" - -if [[ -f "${PIDFILE}" ]]; then - PID=$(cat "${PIDFILE}") - echo "Cleaning up old PID file: ${PIDFILE}" - kill "${PID}" &> /dev/null || true - rm "${PIDFILE}" -fi - -PASSWORD=$("${SECRETS}" -k8s:${PASSWORD} -EOF -chmod go= "${SECRETS}" - -USER_CONFIG= -if [[ "$(id -u)" == "0" ]]; then - USER_CONFIG=" uid = 0"$'\n'" gid = 0" -fi - -cat <"${CONFFILE}" -pid file = ${PIDFILE} -use chroot = no -log file = /dev/stdout -reverse lookup = no -munge symlinks = no -port = 8730 -[k8s] - numeric ids = true - $USER_CONFIG - hosts deny = * - hosts allow = ${ALLOW} ${ALLOW_HOST-} - auth users = k8s - secrets file = ${SECRETS} - read only = false - path = ${VOLUME} - filter = - /_tmp/ -EOF - -exec /usr/bin/rsync --no-detach --daemon --config="${CONFFILE}" "$@" diff --git a/build/common.sh b/build/common.sh index 2bd3cd052272c..b7def19220e24 100755 --- a/build/common.sh +++ b/build/common.sh @@ -38,7 +38,6 @@ KUBE_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P) source "${KUBE_ROOT}/hack/lib/init.sh" # Constants -readonly KUBE_BUILD_IMAGE_REPO=kube-build KUBE_BUILD_IMAGE_CROSS_TAG="${KUBE_CROSS_VERSION:-"$(cat "${KUBE_ROOT}/build/build-image/cross/VERSION")"}" readonly KUBE_BUILD_IMAGE_CROSS_TAG @@ -46,21 +45,13 @@ readonly KUBE_DOCKER_REGISTRY="${KUBE_DOCKER_REGISTRY:-registry.k8s.io}" KUBE_BASE_IMAGE_REGISTRY="${KUBE_BASE_IMAGE_REGISTRY:-registry.k8s.io/build-image}" readonly KUBE_BASE_IMAGE_REGISTRY -# This version number is used to cause everyone to rebuild their data containers -# and build image. This is especially useful for automated build systems like -# Jenkins. -# -# Increment/change this number if you change the build image (anything under -# build/build-image) or change the set of volumes in the data container. -KUBE_BUILD_IMAGE_VERSION_BASE="$(cat "${KUBE_ROOT}/build/build-image/VERSION")" -readonly KUBE_BUILD_IMAGE_VERSION_BASE -readonly KUBE_BUILD_IMAGE_VERSION="${KUBE_BUILD_IMAGE_VERSION_BASE}-${KUBE_BUILD_IMAGE_CROSS_TAG}" - # Make it possible to override the `kube-cross` image, and tag independent of `KUBE_BASE_IMAGE_REGISTRY` KUBE_CROSS_IMAGE="${KUBE_CROSS_IMAGE:-"${KUBE_BASE_IMAGE_REGISTRY}/kube-cross"}" readonly KUBE_CROSS_IMAGE KUBE_CROSS_VERSION="${KUBE_CROSS_VERSION:-"${KUBE_BUILD_IMAGE_CROSS_TAG}"}" readonly KUBE_CROSS_VERSION +KUBE_CROSS_CONTAINER_ROOT="/go/src/k8s.io/kubernetes" +readonly KUBE_CROSS_CONTAINER_ROOT # Here we map the output directories across both the local and remote _output # directories: @@ -88,26 +79,40 @@ readonly REMOTE_OUTPUT_SUBPATH="${REMOTE_OUTPUT_ROOT}/dockerized" readonly REMOTE_OUTPUT_BINPATH="${REMOTE_OUTPUT_SUBPATH}/bin" readonly REMOTE_OUTPUT_GOPATH="${REMOTE_OUTPUT_SUBPATH}/go" -# This is the port on the workstation host to expose RSYNC on. Set this if you -# are doing something fancy with ssh tunneling. -readonly KUBE_RSYNC_PORT="${KUBE_RSYNC_PORT:-}" - -# This is the port that rsync is running on *inside* the container. This may be -# mapped to KUBE_RSYNC_PORT via docker networking. -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.8.7 +readonly __default_go_runner_version=v2.4.0-go1.25.6-bookworm.0 readonly __default_setcap_version=bookworm-v1.0.6 -# These are the base images for the Docker-wrapped binaries. +# The default image for all binaries which are dynamically linked. +# Includes everything that is required by kube-proxy, which uses it +# by default. Other commands only use this when dynamically linking +# them gets requested. +readonly __default_dynamic_base_image="$KUBE_BASE_IMAGE_REGISTRY/distroless-iptables:$__default_distroless_iptables_version" + +# KUBE_GORUNNER_IMAGE is the default image for commands which are built statically. +# It can be overridden to change the image for all such commands. +# When the per-command env variable is set, that env variable is +# used without considering KUBE_GORUNNER_IMAGE. readonly KUBE_GORUNNER_IMAGE="${KUBE_GORUNNER_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/go-runner:$__default_go_runner_version}" -readonly KUBE_APISERVER_BASE_IMAGE="${KUBE_APISERVER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}" -readonly KUBE_CONTROLLER_MANAGER_BASE_IMAGE="${KUBE_CONTROLLER_MANAGER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}" -readonly KUBE_SCHEDULER_BASE_IMAGE="${KUBE_SCHEDULER_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}" -readonly KUBE_PROXY_BASE_IMAGE="${KUBE_PROXY_BASE_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/distroless-iptables:$__default_distroless_iptables_version}" -readonly KUBECTL_BASE_IMAGE="${KUBECTL_BASE_IMAGE:-$KUBE_GORUNNER_IMAGE}" + +# __default_base_image takes the canonical build target for a Kubernetes command (e.g. k8s.io/kubernetes/cmd/kube-scheduler) +# and prints the right default base image for it, depending on whether that command gets built dynamically or statically. +__default_base_image() { + if kube::golang::is_statically_linked "$1"; then + echo "$KUBE_GORUNNER_IMAGE" + else + echo "$__default_dynamic_base_image" + fi +} + +# These are the base images for the Docker-wrapped binaries. +# These can be overridden on a case-by-case basis. +readonly KUBE_APISERVER_BASE_IMAGE="${KUBE_APISERVER_BASE_IMAGE:-$(__default_base_image k8s.io/kubernetes/cmd/kube-apiserver)}" +readonly KUBE_CONTROLLER_MANAGER_BASE_IMAGE="${KUBE_CONTROLLER_MANAGER_BASE_IMAGE:-$(__default_base_image k8s.io/kubernetes/cmd/kube-controller-manager)}" +readonly KUBE_SCHEDULER_BASE_IMAGE="${KUBE_SCHEDULER_BASE_IMAGE:-$(__default_base_image k8s.io/kubernetes/cmd/kube-scheduler)}" +readonly KUBE_PROXY_BASE_IMAGE="${KUBE_PROXY_BASE_IMAGE:-$__default_dynamic_base_image}" +readonly KUBECTL_BASE_IMAGE="${KUBECTL_BASE_IMAGE:-$(__default_base_image k8s.io/kubernetes/cmd/kubectl)}" # This is the image used in a multi-stage build to apply capabilities to Docker-wrapped binaries. readonly KUBE_BUILD_SETCAP_IMAGE="${KUBE_BUILD_SETCAP_IMAGE:-$KUBE_BASE_IMAGE_REGISTRY/setcap:$__default_setcap_version}" @@ -136,6 +141,23 @@ kube::build::get_docker_wrapped_binaries() { # --------------------------------------------------------------------------- # Basic setup functions +# Set up dynamic constants for build environment. +# This function sets up variables that are needed by both verification and cleaning. +# +# Vars set: +# KUBE_ROOT_HASH +# KUBE_BUILD_CONTAINER_NAME_BASE +# KUBE_BUILD_CONTAINER_NAME +function kube::build::setup_vars() { + KUBE_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true) + KUBE_ROOT_HASH=$(kube::build::short_hash "${HOSTNAME:-}:${KUBE_ROOT}:${KUBE_GIT_BRANCH}") + KUBE_BUILD_CONTAINER_NAME_BASE="kube-build-${KUBE_ROOT_HASH}" + # 6 here is out of a wild excess of caution to match previous behavior where + # this was the kube-build image version which surfaced in the name of the container + # the last real image version was 5 + KUBE_BUILD_CONTAINER_NAME="${KUBE_BUILD_CONTAINER_NAME_BASE}-6" +} + # Verify that the right utilities and such are installed for building Kube. Set # up some dynamic constants. # Args: @@ -143,23 +165,13 @@ kube::build::get_docker_wrapped_binaries() { # # Vars set: # KUBE_ROOT_HASH -# KUBE_BUILD_IMAGE_TAG_BASE -# KUBE_BUILD_IMAGE_TAG -# KUBE_BUILD_IMAGE # KUBE_BUILD_CONTAINER_NAME_BASE # KUBE_BUILD_CONTAINER_NAME -# KUBE_DATA_CONTAINER_NAME_BASE -# KUBE_DATA_CONTAINER_NAME -# KUBE_RSYNC_CONTAINER_NAME_BASE -# KUBE_RSYNC_CONTAINER_NAME -# DOCKER_MOUNT_ARGS -# LOCAL_OUTPUT_BUILD_CONTEXT # shellcheck disable=SC2120 # optional parameters function kube::build::verify_prereqs() { local -r require_docker=${1:-true} kube::log::status "Verifying Prerequisites...." kube::build::ensure_tar || return 1 - kube::build::ensure_rsync || return 1 if ${require_docker}; then kube::build::ensure_docker_in_path || return 1 if kube::build::is_osx; then @@ -173,19 +185,7 @@ function kube::build::verify_prereqs() { fi fi - KUBE_GIT_BRANCH=$(git symbolic-ref --short -q HEAD 2>/dev/null || true) - KUBE_ROOT_HASH=$(kube::build::short_hash "${HOSTNAME:-}:${KUBE_ROOT}:${KUBE_GIT_BRANCH}") - KUBE_BUILD_IMAGE_TAG_BASE="build-${KUBE_ROOT_HASH}" - KUBE_BUILD_IMAGE_TAG="${KUBE_BUILD_IMAGE_TAG_BASE}-${KUBE_BUILD_IMAGE_VERSION}" - KUBE_BUILD_IMAGE="${KUBE_BUILD_IMAGE_REPO}:${KUBE_BUILD_IMAGE_TAG}" - KUBE_BUILD_CONTAINER_NAME_BASE="kube-build-${KUBE_ROOT_HASH}" - KUBE_BUILD_CONTAINER_NAME="${KUBE_BUILD_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}" - KUBE_RSYNC_CONTAINER_NAME_BASE="kube-rsync-${KUBE_ROOT_HASH}" - KUBE_RSYNC_CONTAINER_NAME="${KUBE_RSYNC_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}" - KUBE_DATA_CONTAINER_NAME_BASE="kube-build-data-${KUBE_ROOT_HASH}" - KUBE_DATA_CONTAINER_NAME="${KUBE_DATA_CONTAINER_NAME_BASE}-${KUBE_BUILD_IMAGE_VERSION}" - DOCKER_MOUNT_ARGS=(--volumes-from "${KUBE_DATA_CONTAINER_NAME}") - LOCAL_OUTPUT_BUILD_CONTEXT="${LOCAL_OUTPUT_IMAGE_STAGING}/${KUBE_BUILD_IMAGE}" + kube::build::setup_vars kube::version::get_version_vars kube::version::save_version_vars "${KUBE_ROOT}/.dockerized-kube-version-defs" @@ -219,13 +219,6 @@ function kube::build::is_gnu_sed() { [[ $(sed --version 2>&1) == *GNU* ]] } -function kube::build::ensure_rsync() { - if [[ -z "$(which rsync)" ]]; then - kube::log::error "Can't find 'rsync' in PATH, please fix and retry." - return 1 - fi -} - function kube::build::ensure_docker_in_path() { if [[ -z "$(which docker)" ]]; then kube::log::error "Can't find 'docker' in PATH, please fix and retry." @@ -276,29 +269,6 @@ function kube::build::docker_image_exists() { [[ $("${DOCKER[@]}" images -q "${1}:${2}") ]] } -# Delete all images that match a tag prefix except for the "current" version -# -# $1: The image repo/name -# $2: The tag base. We consider any image that matches $2* -# $3: The current image not to delete if provided -function kube::build::docker_delete_old_images() { - # In Docker 1.12, we can replace this with - # docker images "$1" --format "{{.Tag}}" - for tag in $("${DOCKER[@]}" images "${1}" | tail -n +2 | awk '{print $2}') ; do - if [[ "${tag}" != "${2}"* ]] ; then - V=3 kube::log::status "Keeping image ${1}:${tag}" - continue - fi - - if [[ -z "${3:-}" || "${tag}" != "${3}" ]] ; then - V=2 kube::log::status "Deleting image ${1}:${tag}" - "${DOCKER[@]}" rmi "${1}:${tag}" >/dev/null - else - V=3 kube::log::status "Keeping image ${1}:${tag}" - fi - done -} - # Stop and delete all containers that match a pattern # # $1: The base container prefix @@ -352,6 +322,10 @@ function kube::build::destroy_container() { "${DOCKER[@]}" rm -f -v "$1" >/dev/null 2>&1 || true } +function kube::build::is_docker_rootless() { + "${DOCKER[@]}" info --format '{{json .SecurityOptions}}' | grep -q "name=rootless" +} + # --------------------------------------------------------------------------- # Building @@ -359,9 +333,6 @@ function kube::build::destroy_container() { function kube::build::clean() { if kube::build::has_docker ; then kube::build::docker_delete_old_containers "${KUBE_BUILD_CONTAINER_NAME_BASE}" - kube::build::docker_delete_old_containers "${KUBE_RSYNC_CONTAINER_NAME_BASE}" - kube::build::docker_delete_old_containers "${KUBE_DATA_CONTAINER_NAME_BASE}" - kube::build::docker_delete_old_images "${KUBE_BUILD_IMAGE_REPO}" "${KUBE_BUILD_IMAGE_TAG_BASE}" V=2 kube::log::status "Cleaning all untagged docker images" "${DOCKER[@]}" rmi "$("${DOCKER[@]}" images -q --filter 'dangling=true')" 2> /dev/null || true @@ -369,117 +340,15 @@ function kube::build::clean() { if [[ -d "${LOCAL_OUTPUT_ROOT}" ]]; then kube::log::status "Removing _output directory" - # this ensures we can clean _output/local/go/cache which is not rw by default - chmod -R +w "${LOCAL_OUTPUT_ROOT}" - rm -rf "${LOCAL_OUTPUT_ROOT}" - fi -} - -# Set up the context directory for the kube-build image and build it. -function kube::build::build_image() { - mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}" - # Make sure the context directory owned by the right user for syncing sources to container. - chown -R "${USER_ID}":"${GROUP_ID}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" - - cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/" - chmod u+w "${LOCAL_OUTPUT_BUILD_CONTEXT}/localtime" - - cp "${KUBE_ROOT}/build/build-image/Dockerfile" "${LOCAL_OUTPUT_BUILD_CONTEXT}/Dockerfile" - cp "${KUBE_ROOT}/build/build-image/rsyncd.sh" "${LOCAL_OUTPUT_BUILD_CONTEXT}/" - dd if=/dev/urandom bs=512 count=1 2>/dev/null | LC_ALL=C tr -dc 'A-Za-z0-9' | dd bs=32 count=1 2>/dev/null > "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" - chmod go= "${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" - - kube::build::docker_build "${KUBE_BUILD_IMAGE}" "${LOCAL_OUTPUT_BUILD_CONTEXT}" 'false' "--build-arg=KUBE_CROSS_IMAGE=${KUBE_CROSS_IMAGE} --build-arg=KUBE_CROSS_VERSION=${KUBE_CROSS_VERSION}" - - # Clean up old versions of everything - kube::build::docker_delete_old_containers "${KUBE_BUILD_CONTAINER_NAME_BASE}" "${KUBE_BUILD_CONTAINER_NAME}" - kube::build::docker_delete_old_containers "${KUBE_RSYNC_CONTAINER_NAME_BASE}" "${KUBE_RSYNC_CONTAINER_NAME}" - kube::build::docker_delete_old_containers "${KUBE_DATA_CONTAINER_NAME_BASE}" "${KUBE_DATA_CONTAINER_NAME}" - kube::build::docker_delete_old_images "${KUBE_BUILD_IMAGE_REPO}" "${KUBE_BUILD_IMAGE_TAG_BASE}" "${KUBE_BUILD_IMAGE_TAG}" - - kube::build::ensure_data_container - kube::build::sync_to_container -} - -# Build a docker image from a Dockerfile. -# $1 is the name of the image to build -# $2 is the location of the "context" directory, with the Dockerfile at the root. -# $3 is the value to set the --pull flag for docker build; true by default -# $4 is the set of --build-args for docker. -function kube::build::docker_build() { - kube::util::ensure-docker-buildx - - local -r image=$1 - local -r context_dir=$2 - local -r pull="${3:-true}" - local build_args - IFS=" " read -r -a build_args <<< "$4" - readonly build_args - local -ra build_cmd=("${DOCKER[@]}" buildx build --load -t "${image}" "--pull=${pull}" "${build_args[@]}" "${context_dir}") - - kube::log::status "Building Docker image ${image}" - local docker_output - docker_output=$(DOCKER_CLI_EXPERIMENTAL=enabled "${build_cmd[@]}" 2>&1) || { - cat <&2 -+++ Docker build command failed for ${image} - -${docker_output} - -To retry manually, run: - -DOCKER_CLI_EXPERIMENTAL=enabled ${build_cmd[*]} - -EOF - return 1 - } -} - -function kube::build::ensure_data_container() { - # If the data container exists AND exited successfully, we can use it. - # Otherwise nuke it and start over. - local ret=0 - local code=0 - - code=$(docker inspect \ - -f '{{.State.ExitCode}}' \ - "${KUBE_DATA_CONTAINER_NAME}" 2>/dev/null) || ret=$? - if [[ "${ret}" == 0 && "${code}" != 0 ]]; then - kube::build::destroy_container "${KUBE_DATA_CONTAINER_NAME}" - ret=1 - fi - if [[ "${ret}" != 0 ]]; then - kube::log::status "Creating data container ${KUBE_DATA_CONTAINER_NAME}" - # We have to ensure the directory exists, or else the docker run will - # create it as root. - mkdir -p "${LOCAL_OUTPUT_GOPATH}" - # We want this to run as root to be able to chown, so non-root users can - # later use the result as a data container. This run both creates the data - # container and chowns the GOPATH. + # This ensures we can clean _output/local/go/cache which is not rw by default. # - # The data container creates volumes for all of the directories that store - # intermediates for the Go build. This enables incremental builds across - # Docker sessions. The *_cgo paths are re-compiled versions of the go std - # libraries for true static building. - local -ra docker_cmd=( - "${DOCKER[@]}" run - --volume "${REMOTE_ROOT}" # white-out the whole output dir - --volume /usr/local/go/pkg/linux_386_cgo - --volume /usr/local/go/pkg/linux_amd64_cgo - --volume /usr/local/go/pkg/linux_arm_cgo - --volume /usr/local/go/pkg/linux_arm64_cgo - --volume /usr/local/go/pkg/linux_ppc64le_cgo - --volume /usr/local/go/pkg/darwin_amd64_cgo - --volume /usr/local/go/pkg/darwin_386_cgo - --volume /usr/local/go/pkg/windows_amd64_cgo - --volume /usr/local/go/pkg/windows_386_cgo - --name "${KUBE_DATA_CONTAINER_NAME}" - --hostname "${HOSTNAME}" - "${KUBE_BUILD_IMAGE}" - chown -R "${USER_ID}":"${GROUP_ID}" - "${REMOTE_ROOT}" - /usr/local/go/pkg/ - ) - "${docker_cmd[@]}" + # We only do this path specifically instead of the entire output root + # because recursive chmod is slow. + # We don't need to do this at all for dockerized builds + if [[ -d "${LOCAL_OUTPUT_ROOT}/local/go/cache" ]]; then + chmod -R +w "${LOCAL_OUTPUT_ROOT}/local/go/cache" + fi + rm -rf "${LOCAL_OUTPUT_ROOT}" fi } @@ -502,12 +371,12 @@ function kube::build::run_build_command_ex() { local -a docker_run_opts=( "--name=${container_name}" - "--user=$(id -u):$(id -g)" "--hostname=${HOSTNAME}" "-e=GOPROXY=${GOPROXY}" - "${DOCKER_MOUNT_ARGS[@]}" ) + kube::build::is_docker_rootless || docker_run_opts+=("--user=$(id -u):$(id -g)") + local detach=false [[ $# != 0 ]] || { echo "Invalid input - please specify docker arguments followed by --." >&2; return 4; } @@ -540,14 +409,29 @@ function kube::build::run_build_command_ex() { --env "KUBE_BUILD_PLATFORMS=${KUBE_BUILD_PLATFORMS:-}" --env "KUBE_CGO_OVERRIDES=' ${KUBE_CGO_OVERRIDES[*]:-} '" --env "KUBE_STATIC_OVERRIDES=' ${KUBE_STATIC_OVERRIDES[*]:-} '" + --env "KUBE_RACE=${KUBE_RACE:-}" --env "FORCE_HOST_GO=${FORCE_HOST_GO:-}" --env "GO_VERSION=${GO_VERSION:-}" --env "GOTOOLCHAIN=${GOTOOLCHAIN:-}" --env "GOFLAGS=${GOFLAGS:-}" --env "GOGCFLAGS=${GOGCFLAGS:-}" --env "SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH:-}" + # mount source code / output dir + --volume "${KUBE_ROOT}:${KUBE_CROSS_CONTAINER_ROOT}" + # env migrated from build-image, we could consider setting this in kube-cross + --env 'KUBE_OUTPUT_SUBPATH=_output/dockerized' + --workdir "${KUBE_CROSS_CONTAINER_ROOT}" + --env 'GIT_AUTHOR_EMAIL=nobody@k8s.io' + --env 'GIT_AUTHOR_NAME=kube-build-image' ) + # if host has localtime, mount it so we log in local time + if [ -f /etc/localtime ]; then + docker_run_opts+=( + --mount 'type=bind,source=/etc/localtime,target=/etc/localtime,readonly' + ) + fi + # use GOLDFLAGS only if it is set explicitly. if [[ -v GOLDFLAGS ]]; then docker_run_opts+=( @@ -560,6 +444,12 @@ function kube::build::run_build_command_ex() { docker_run_opts+=(--cgroup-parent "${DOCKER_CGROUP_PARENT}") fi + # copy KUBE_GIT_VERSION_FILE to .dockerized-kube-version-defs and set environment variable. + if [[ -n "${KUBE_GIT_VERSION_FILE:-}" ]]; then + cp "${KUBE_GIT_VERSION_FILE}" "${KUBE_ROOT}/.dockerized-kube-version-defs" + docker_run_opts+=(--env "KUBE_GIT_VERSION_FILE=${KUBE_CROSS_CONTAINER_ROOT}/.dockerized-kube-version-defs") + fi + # If we have stdin we can run interactive. This allows things like 'shell.sh' # to work. However, if we run this way and don't have stdin, then it ends up # running in a daemon-ish mode. So if we don't have a stdin, we explicitly @@ -571,7 +461,7 @@ function kube::build::run_build_command_ex() { fi local -ra docker_cmd=( - "${DOCKER[@]}" run "${docker_run_opts[@]}" "${KUBE_BUILD_IMAGE}") + "${DOCKER[@]}" run "${docker_run_opts[@]}" "${KUBE_CROSS_IMAGE}:${KUBE_CROSS_VERSION}") # Clean up container from any previous run kube::build::destroy_container "${container_name}" @@ -580,141 +470,3 @@ function kube::build::run_build_command_ex() { kube::build::destroy_container "${container_name}" fi } - -function kube::build::rsync_probe { - # Wait until rsync is up and running. - local tries=20 - while (( tries > 0 )) ; do - if rsync "rsync://k8s@${1}:${2}/" \ - --password-file="${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" \ - &> /dev/null ; then - return 0 - fi - tries=$(( tries - 1)) - sleep 0.1 - done - - return 1 -} - -# Start up the rsync container in the background. This should be explicitly -# stopped with kube::build::stop_rsyncd_container. -# -# This will set the global var KUBE_RSYNC_ADDR to the effective port that the -# rsync daemon can be reached out. -function kube::build::start_rsyncd_container() { - IPTOOL=ifconfig - if kube::build::has_ip ; then - IPTOOL="ip address" - fi - kube::build::stop_rsyncd_container - V=3 kube::log::status "Starting rsyncd container" - kube::build::run_build_command_ex \ - "${KUBE_RSYNC_CONTAINER_NAME}" -p 127.0.0.1:"${KUBE_RSYNC_PORT}":"${KUBE_CONTAINER_RSYNC_PORT}" -d \ - -e ALLOW_HOST="$(${IPTOOL} | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | tr '\n' ' ')" \ - -- /rsyncd.sh >/dev/null - - local mapped_port - if ! mapped_port=$("${DOCKER[@]}" port "${KUBE_RSYNC_CONTAINER_NAME}" "${KUBE_CONTAINER_RSYNC_PORT}" 2> /dev/null | cut -d: -f 2) ; then - kube::log::error "Could not get effective rsync port" - return 1 - fi - - local container_ip - container_ip=$("${DOCKER[@]}" inspect --format '{{ .NetworkSettings.IPAddress }}' "${KUBE_RSYNC_CONTAINER_NAME}") - - # 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 - # machines) we have to talk directly to the container IP. There is no one - # strategy that works in all cases so we test to figure out which situation we - # are in. - if kube::build::rsync_probe 127.0.0.1 "${mapped_port}"; then - KUBE_RSYNC_ADDR="127.0.0.1:${mapped_port}" - return 0 - elif kube::build::rsync_probe "${container_ip}" "${KUBE_CONTAINER_RSYNC_PORT}"; then - KUBE_RSYNC_ADDR="${container_ip}:${KUBE_CONTAINER_RSYNC_PORT}" - return 0 - fi - - kube::log::error "Could not connect to rsync container." - return 1 -} - -function kube::build::stop_rsyncd_container() { - V=3 kube::log::status "Stopping any currently running rsyncd container" - unset KUBE_RSYNC_ADDR - kube::build::destroy_container "${KUBE_RSYNC_CONTAINER_NAME}" -} - -function kube::build::rsync { - local -a rsync_opts=( - --archive - "--password-file=${LOCAL_OUTPUT_BUILD_CONTEXT}/rsyncd.password" - ) - if (( KUBE_VERBOSE >= 6 )); then - rsync_opts+=("-iv") - fi - if (( KUBE_RSYNC_COMPRESS > 0 )); then - rsync_opts+=("--compress-level=${KUBE_RSYNC_COMPRESS}") - fi - V=3 kube::log::status "Running rsync" - rsync "${rsync_opts[@]}" "$@" -} - -# This will launch rsyncd in a container and then sync the source tree to the -# container over the local network. -function kube::build::sync_to_container() { - kube::log::status "Syncing sources to container" - - kube::build::start_rsyncd_container - - # rsync filters are a bit confusing. Here we are syncing everything except - # output only directories and things that are not necessary like the git - # directory and generated files. The '- /' filter prevents rsync - # from trying to set the uid/gid/perms on the root of the sync tree. - # As an exception, we need to sync generated files in staging/, because - # they will not be re-generated by 'make'. Note that the 'H' filtered files - # are hidden from rsync so they will be deleted in the target container if - # they exist. This will allow them to be re-created in the container if - # necessary. - kube::build::rsync \ - --delete \ - --filter='- /_tmp/' \ - --filter='- /_output/' \ - --filter='- /' \ - "${KUBE_ROOT}/" "rsync://k8s@${KUBE_RSYNC_ADDR}/k8s/" - - kube::build::stop_rsyncd_container -} - -# Copy all build results back out. -function kube::build::copy_output() { - kube::log::status "Syncing out of container" - - kube::build::start_rsyncd_container - - # The filter syntax for rsync is a little obscure. It filters on files and - # directories. If you don't go in to a directory you won't find any files - # there. Rules are evaluated in order. The last two rules are a little - # magic. '+ */' says to go in to every directory and '- /**' says to ignore - # any file or directory that isn't already specifically allowed. - # - # We are looking to copy out all of the built binaries along with various - # generated files. - kube::build::rsync \ - --prune-empty-dirs \ - --filter='- /_temp/' \ - --filter='+ /vendor/' \ - --filter='+ /staging/***/Godeps/**' \ - --filter='+ /_output/dockerized/bin/**' \ - --filter='- /_output/dockerized/go/**' \ - --filter='+ zz_generated.*' \ - --filter='+ generated.proto' \ - --filter='+ *.pb.go' \ - --filter='+ types.go' \ - --filter='+ */' \ - --filter='- /**' \ - "rsync://k8s@${KUBE_RSYNC_ADDR}/k8s/" "${KUBE_ROOT}" - - kube::build::stop_rsyncd_container -} diff --git a/build/copy-output.sh b/build/copy-output.sh deleted file mode 100755 index 5829e81f49b26..0000000000000 --- a/build/copy-output.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2014 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. - -# Copies any built binaries (and other generated files) out of the Docker build container. -set -o errexit -set -o nounset -set -o pipefail - -KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source "${KUBE_ROOT}/build/common.sh" - -kube::build::verify_prereqs -kube::build::copy_output diff --git a/build/dependencies.yaml b/build/dependencies.yaml index 3abc723bea0f9..bb37dcc3fa82e 100644 --- a/build/dependencies.yaml +++ b/build/dependencies.yaml @@ -18,7 +18,7 @@ dependencies: # CNI plugins - name: "cni" - version: 1.7.1 + version: 1.8.0 refPaths: - path: cluster/gce/config-common.sh match: WINDOWS_CNI_VERSION= @@ -31,7 +31,7 @@ dependencies: # CoreDNS - name: "coredns-kube-up" - version: 1.12.1 + version: 1.13.1 refPaths: - path: cluster/addons/dns/coredns/coredns.yaml.base match: registry.k8s.io/coredns @@ -41,14 +41,14 @@ dependencies: match: registry.k8s.io/coredns - name: "coredns-kubeadm" - version: 1.12.1 + version: 1.13.1 refPaths: - path: cmd/kubeadm/app/constants/constants.go match: CoreDNSVersion = # CRI Tools - name: "crictl" - version: 1.33.0 + version: 1.34.0 refPaths: - path: cluster/gce/windows/k8s-node-setup.psm1 match: CRICTL_VERSION = @@ -64,7 +64,7 @@ dependencies: # etcd - name: "etcd" - version: 3.6.5 + version: 3.6.6 refPaths: - path: cluster/gce/manifests/etcd.manifest match: etcd_docker_tag|etcd_version @@ -74,6 +74,10 @@ dependencies: match: DefaultEtcdVersion = - path: hack/lib/etcd.sh match: ETCD_VERSION= + - path: staging/src/k8s.io/sample-apiserver/artifacts/example/deployment.yaml + match: registry.k8s.io/etcd + - path: test/utils/image/manifest.go + match: configs\[Etcd\] = Config{list\.GcEtcdRegistry, "etcd", "\d+\.\d+.\d+(-(alpha|beta|rc).\d+)?(-\d+)?"} - name: "etcd-image" version: 3.6.4 @@ -83,7 +87,7 @@ dependencies: - path: cluster/images/etcd/migrate/options.go - name: "node-problem-detector" - version: 0.8.21 + version: 1.34.0 refPaths: - path: test/e2e_node/image_list.go match: const defaultImage @@ -113,31 +117,36 @@ dependencies: # Golang # TODO: this should really be eliminated and controlled by .go-version - name: "golang: upstream version" - version: 1.24.9 + version: 1.25.6 refPaths: - path: .go-version - - path: build/build-image/cross/VERSION - path: staging/publishing/rules.yaml match: 'default-go-version\: \d+.\d+(alpha|beta|rc)?\.?(\d+)?' + # This should ideally be updated to match the golang version + # but we can dynamically fetch go if the base image is out of date. + # This allows us to ship go updates more quickly. + # + # NOTE: To fully patch all binaries, go-runner and the related base images + # should also be updated, but go-runner is much harder to exploit and has + # far less relevancy to go updates for Kubernetes more generally. + - name: "registry.k8s.io/kube-cross: dependents" + version: v1.35.0-go1.25.6-bullseye.0 + refPaths: + - path: build/build-image/cross/VERSION + # Golang pre-releases are denoted as `1.y` # Example: go1.16rc1 # # This entry is a stub of the major version to allow dependency checks to # pass when building Kubernetes using a pre-release of Golang. - - name: "golang: 1." - version: 1.24 + version: 1.25 refPaths: - path: build/build-image/cross/VERSION - path: hack/lib/golang.sh 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 - refPaths: - - path: build/build-image/cross/VERSION - # Base images - name: "registry.k8s.io/debian-base: dependents" version: bookworm-v1.0.6 @@ -170,7 +179,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.8.7 refPaths: - path: build/common.sh match: __default_distroless_iptables_version= @@ -178,7 +187,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.25.6-bookworm.0 refPaths: - path: build/common.sh match: __default_go_runner_version= @@ -234,6 +243,8 @@ dependencies: match: registry.k8s.io\/pause:\d+\.\d+ - path: test/utils/image/manifest.go match: configs\[Pause\] = Config{list\.GcRegistry, "pause", "\d+\.\d+(.\d+)?"} + - path: test/images/agnhost/fakeregistryserver/images.txt + match: pause\s - name: "registry.k8s.io/build-image/setcap: dependents" version: bookworm-v1.0.6 diff --git a/build/lib/release.sh b/build/lib/release.sh index 1bf19257d5350..4a086bdb39d94 100644 --- a/build/lib/release.sh +++ b/build/lib/release.sh @@ -332,7 +332,7 @@ function kube::release::create_docker_images_for_server() { docker_file_path="${KUBE_ROOT}/build/server-image/${binary_name}/Dockerfile" fi - kube::log::status "Starting docker build for image: ${binary_name}-${arch}" + kube::log::status "Starting docker build for image: ${binary_name}-${arch} with base ${base_image}" ( rm -rf "${docker_build_path}" mkdir -p "${docker_build_path}" diff --git a/build/make-build-image.sh b/build/make-build-image.sh deleted file mode 100755 index 9c7ba3a603f4b..0000000000000 --- a/build/make-build-image.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2014 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. - -# Build the docker image necessary for building Kubernetes -# -# This script will package the parts of the repo that we need to build -# Kubernetes into a tar file and put it in the right place in the output -# directory. It will then copy over the Dockerfile and build the kube-build -# image. -set -o errexit -set -o nounset -set -o pipefail - -KUBE_ROOT="$(dirname "${BASH_SOURCE[0]}")/.." -source "${KUBE_ROOT}/build/common.sh" - -kube::build::verify_prereqs -kube::build::build_image diff --git a/build/make-clean.sh b/build/make-clean.sh index b3f567490a6b8..7fc34751bc7a3 100755 --- a/build/make-clean.sh +++ b/build/make-clean.sh @@ -22,5 +22,5 @@ set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${KUBE_ROOT}/build/common.sh" -kube::build::verify_prereqs false +kube::build::setup_vars kube::build::clean diff --git a/build/pause/Dockerfile.Rhel b/build/pause/Dockerfile.Rhel index f73013e3160ef..6a404b4861813 100644 --- a/build/pause/Dockerfile.Rhel +++ b/build/pause/Dockerfile.Rhel @@ -1,4 +1,4 @@ -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS builder WORKDIR /go/src/github.com/openshift/kubernetes/build/pause COPY . . RUN mkdir -p bin && \ diff --git a/build/release-images.sh b/build/release-images.sh index afaecb1864bb5..d275aa616c5cf 100755 --- a/build/release-images.sh +++ b/build/release-images.sh @@ -36,9 +36,5 @@ if [[ -n "${KUBE_EXTRA_WHAT:-}" ]]; then fi kube::build::verify_prereqs -kube::build::build_image kube::build::run_build_command make all WHAT="${CMD_TARGETS}" KUBE_BUILD_PLATFORMS="${KUBE_SERVER_PLATFORMS[*]}" DBG="${DBG:-}" - -kube::build::copy_output - kube::release::build_server_images diff --git a/build/release.sh b/build/release.sh index 20010d29aaa40..c2790e30525eb 100755 --- a/build/release.sh +++ b/build/release.sh @@ -32,7 +32,6 @@ source "${KUBE_ROOT}/build/lib/release.sh" KUBE_RELEASE_RUN_TESTS=${KUBE_RELEASE_RUN_TESTS-y} kube::build::verify_prereqs -kube::build::build_image kube::build::run_build_command make cross if [[ $KUBE_RELEASE_RUN_TESTS =~ ^[yY]$ ]]; then @@ -40,6 +39,4 @@ if [[ $KUBE_RELEASE_RUN_TESTS =~ ^[yY]$ ]]; then kube::build::run_build_command make test-integration fi -kube::build::copy_output - kube::release::package_tarballs diff --git a/build/run.sh b/build/run.sh index c0eb0b83270b0..e342d6d298f47 100755 --- a/build/run.sh +++ b/build/run.sh @@ -25,25 +25,18 @@ set -o pipefail KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "$KUBE_ROOT/build/common.sh" +# This is no longer supported, warn if set to non-default value +KUBE_RUN_COPY_OUTPUT="${KUBE_RUN_COPY_OUTPUT:-y}" +# we previously accepted an explicit both y and Y +if [[ ! "${KUBE_RUN_COPY_OUTPUT}" =~ ^[yY]$ ]]; then + kube::log::error "KUBE_RUN_COPY_OUTPUT no longer means anything as we bind-mount instead of rsyncing, so output is always persisted" +fi + # Allow running without docker (e.g. in openshift ci) if [[ "${OS_RUN_WITHOUT_DOCKER:-}" ]]; then "${@}" exit 0 fi -KUBE_RUN_COPY_OUTPUT="${KUBE_RUN_COPY_OUTPUT:-y}" - kube::build::verify_prereqs -kube::build::build_image - -if [[ ${KUBE_RUN_COPY_OUTPUT} =~ ^[yY]$ ]]; then - kube::log::status "Output from this container will be rsynced out upon completion. Set KUBE_RUN_COPY_OUTPUT=n to disable." -else - kube::log::status "Output from this container will NOT be rsynced out upon completion. Set KUBE_RUN_COPY_OUTPUT=y to enable." -fi - kube::build::run_build_command "$@" - -if [[ ${KUBE_RUN_COPY_OUTPUT} =~ ^[yY]$ ]]; then - kube::build::copy_output -fi diff --git a/build/server-image/Dockerfile b/build/server-image/Dockerfile index 78e01c3647457..09d2cd1e6916e 100644 --- a/build/server-image/Dockerfile +++ b/build/server-image/Dockerfile @@ -19,4 +19,4 @@ ARG BINARY FROM "${BASEIMAGE}" -COPY ${BINARY} /usr/local/bin/${BINARY} +COPY --chmod=755 ${BINARY} /usr/local/bin/${BINARY} diff --git a/build/server-image/kube-apiserver/Dockerfile b/build/server-image/kube-apiserver/Dockerfile index d5ac37d14f1bd..27129d29bd9d6 100644 --- a/build/server-image/kube-apiserver/Dockerfile +++ b/build/server-image/kube-apiserver/Dockerfile @@ -20,7 +20,7 @@ ARG SETCAP_IMAGE # to setup qemu for the builder. FROM --platform=linux/$BUILDARCH ${SETCAP_IMAGE} ARG BINARY -COPY ${BINARY} /${BINARY} +COPY --chmod=755 ${BINARY} /${BINARY} # We apply cap_net_bind_service so that kube-apiserver can be run as # non-root and still listen on port less than 1024 RUN setcap cap_net_bind_service=+ep /${BINARY} diff --git a/build/server-image/kubectl/Dockerfile b/build/server-image/kubectl/Dockerfile index 0c93ca6a4e11b..df6073c5456f5 100644 --- a/build/server-image/kubectl/Dockerfile +++ b/build/server-image/kubectl/Dockerfile @@ -19,5 +19,5 @@ ARG BINARY FROM "${BASEIMAGE}" -COPY ${BINARY} /bin/ +COPY --chmod=755 ${BINARY} /bin/ ENTRYPOINT ["/bin/kubectl"] diff --git a/build/tools.go b/build/tools.go index 60e1880c8031f..ad60ce3a675e0 100644 --- a/build/tools.go +++ b/build/tools.go @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This package imports things required by build scripts and test packages of submodules, to force `go mod` to see them as dependencies +// This package imports things required by build scripts, to force `go mod` to see them as dependencies package tools import ( @@ -28,7 +28,4 @@ import ( _ "k8s.io/code-generator/cmd/go-to-protobuf" _ "k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo" _ "k8s.io/kube-openapi/cmd/openapi-gen" - - // submodule test dependencies - _ "github.com/armon/go-socks5" // for staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper_test.go ) diff --git a/cluster/addons/dns/coredns/coredns.yaml.base b/cluster/addons/dns/coredns/coredns.yaml.base index df61a856d7eb3..9cce5c34537c0 100644 --- a/cluster/addons/dns/coredns/coredns.yaml.base +++ b/cluster/addons/dns/coredns/coredns.yaml.base @@ -133,7 +133,7 @@ spec: kubernetes.io/os: linux containers: - name: coredns - image: registry.k8s.io/coredns/coredns:v1.12.1 + image: registry.k8s.io/coredns/coredns:v1.13.1 imagePullPolicy: IfNotPresent resources: limits: diff --git a/cluster/addons/dns/coredns/coredns.yaml.in b/cluster/addons/dns/coredns/coredns.yaml.in index 525737b81a3a6..ee07de6c328ba 100644 --- a/cluster/addons/dns/coredns/coredns.yaml.in +++ b/cluster/addons/dns/coredns/coredns.yaml.in @@ -133,7 +133,7 @@ spec: kubernetes.io/os: linux containers: - name: coredns - image: registry.k8s.io/coredns/coredns:v1.12.1 + image: registry.k8s.io/coredns/coredns:v1.13.1 imagePullPolicy: IfNotPresent resources: limits: diff --git a/cluster/addons/dns/coredns/coredns.yaml.sed b/cluster/addons/dns/coredns/coredns.yaml.sed index 6504762d8e104..5df370800c978 100644 --- a/cluster/addons/dns/coredns/coredns.yaml.sed +++ b/cluster/addons/dns/coredns/coredns.yaml.sed @@ -133,7 +133,7 @@ spec: kubernetes.io/os: linux containers: - name: coredns - image: registry.k8s.io/coredns/coredns:v1.12.1 + image: registry.k8s.io/coredns/coredns:v1.13.1 imagePullPolicy: IfNotPresent resources: limits: diff --git a/cluster/addons/ip-masq-agent/ip-masq-agent.yaml b/cluster/addons/ip-masq-agent/ip-masq-agent.yaml index 341966825e631..60302036ec046 100644 --- a/cluster/addons/ip-masq-agent/ip-masq-agent.yaml +++ b/cluster/addons/ip-masq-agent/ip-masq-agent.yaml @@ -29,7 +29,7 @@ spec: hostNetwork: true containers: - name: ip-masq-agent - image: registry.k8s.io/networking/ip-masq-agent:v2.9.3 + image: registry.k8s.io/networking/ip-masq-agent:v2.12.0 args: - --masq-chain=IP-MASQ - --nomasq-all-reserved-ranges diff --git a/cluster/addons/kube-network-policies/kube-network-policies.yaml b/cluster/addons/kube-network-policies/kube-network-policies.yaml index 0dd1abd284903..de416a73dec11 100644 --- a/cluster/addons/kube-network-policies/kube-network-policies.yaml +++ b/cluster/addons/kube-network-policies/kube-network-policies.yaml @@ -27,7 +27,7 @@ spec: serviceAccountName: kube-network-policies containers: - name: kube-network-policies - image: registry.k8s.io/networking/kube-network-policies:v0.7.0 + image: registry.k8s.io/networking/kube-network-policies:v0.9.2 command: - /bin/netpol - --v=4 diff --git a/cluster/addons/metrics-server/metrics-server-deployment.yaml b/cluster/addons/metrics-server/metrics-server-deployment.yaml index 89169d0f41763..b979e9f21e831 100644 --- a/cluster/addons/metrics-server/metrics-server-deployment.yaml +++ b/cluster/addons/metrics-server/metrics-server-deployment.yaml @@ -23,23 +23,23 @@ data: apiVersion: apps/v1 kind: Deployment metadata: - name: metrics-server-v0.7.2 + name: metrics-server-v0.8.0 namespace: kube-system labels: k8s-app: metrics-server addonmanager.kubernetes.io/mode: Reconcile - version: v0.7.2 + version: v0.8.0 spec: selector: matchLabels: k8s-app: metrics-server - version: v0.7.2 + version: v0.8.0 template: metadata: name: metrics-server labels: k8s-app: metrics-server - version: v0.7.2 + version: v0.8.0 spec: securityContext: seccompProfile: @@ -50,7 +50,7 @@ spec: kubernetes.io/os: linux containers: - name: metrics-server - image: registry.k8s.io/metrics-server/metrics-server:v0.7.2 + image: registry.k8s.io/metrics-server/metrics-server:v0.8.0 command: - /metrics-server - --metric-resolution=15s @@ -81,7 +81,7 @@ spec: - mountPath: /tmp name: tmp-dir - name: metrics-server-nanny - image: registry.k8s.io/autoscaling/addon-resizer:1.8.14 + image: registry.k8s.io/autoscaling/addon-resizer:1.8.20 resources: limits: cpu: 100m @@ -109,7 +109,7 @@ spec: - --memory={{ base_metrics_server_memory }} - --extra-memory={{ metrics_server_memory_per_node }}Mi - --threshold=5 - - --deployment=metrics-server-v0.7.2 + - --deployment=metrics-server-v0.8.0 - --container=metrics-server - --poll-period=30000 - --estimator=exponential diff --git a/cluster/addons/node-problem-detector/npd.yaml b/cluster/addons/node-problem-detector/npd.yaml index 20af9a1aa49d5..838106be1bf1f 100644 --- a/cluster/addons/node-problem-detector/npd.yaml +++ b/cluster/addons/node-problem-detector/npd.yaml @@ -30,24 +30,24 @@ metadata: namespace: kube-system labels: app.kubernetes.io/name: node-problem-detector - app.kubernetes.io/version: v0.8.21 + app.kubernetes.io/version: v1.34.0 addonmanager.kubernetes.io/mode: Reconcile spec: selector: matchLabels: app.kubernetes.io/name: node-problem-detector - app.kubernetes.io/version: v0.8.21 + app.kubernetes.io/version: v1.34.0 template: metadata: labels: app.kubernetes.io/name: node-problem-detector - app.kubernetes.io/version: v0.8.21 + app.kubernetes.io/version: v1.34.0 spec: nodeSelectors: kubernetes.io/os: linux containers: - name: node-problem-detector - image: registry.k8s.io/node-problem-detector/node-problem-detector:v0.8.21 + image: registry.k8s.io/node-problem-detector/node-problem-detector:v1.34.0 command: - "/bin/sh" - "-c" diff --git a/cluster/gce/config-common.sh b/cluster/gce/config-common.sh index 101848f3ffbfd..e50985fc54bcd 100644 --- a/cluster/gce/config-common.sh +++ b/cluster/gce/config-common.sh @@ -136,7 +136,7 @@ export WINDOWS_CNI_CONFIG_DIR="${WINDOWS_K8S_DIR}\cni\config" # CNI storage path for Windows nodes export WINDOWS_CNI_STORAGE_PATH="https://github.com/containernetworking/plugins/releases/download" # CNI version for Windows nodes -export WINDOWS_CNI_VERSION="v1.7.1" +export WINDOWS_CNI_VERSION="v1.8.0" # Pod manifests directory for Windows nodes on Windows nodes. export WINDOWS_MANIFESTS_DIR="${WINDOWS_K8S_DIR}\manifests" # Directory where cert/key files will be stores on Windows nodes. diff --git a/cluster/gce/config-default.sh b/cluster/gce/config-default.sh index 2e26d07786c0c..a104d3c1f8083 100755 --- a/cluster/gce/config-default.sh +++ b/cluster/gce/config-default.sh @@ -88,13 +88,14 @@ fi # By default, the latest image from the image family will be used unless an # explicit image will be set. GCI_VERSION=${KUBE_GCI_VERSION:-} -IMAGE_FAMILY=${KUBE_IMAGE_FAMILY:-cos-121-lts} +IMAGE_FAMILY=${KUBE_GCE_IMAGE_FAMILY:-cos-121-lts} +IMAGE_PROJECT=${KUBE_GCE_IMAGE_PROJECT:-cos-cloud} export MASTER_IMAGE=${KUBE_GCE_MASTER_IMAGE:-} export MASTER_IMAGE_FAMILY=${KUBE_GCE_MASTER_IMAGE_FAMILY:-${IMAGE_FAMILY}} -export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-cos-cloud} +export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-${IMAGE_PROJECT}} export NODE_IMAGE=${KUBE_GCE_NODE_IMAGE:-${GCI_VERSION}} export NODE_IMAGE_FAMILY=${KUBE_GCE_NODE_IMAGE_FAMILY:-${IMAGE_FAMILY}} -export NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-cos-cloud} +export NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-${IMAGE_PROJECT}} export NODE_SERVICE_ACCOUNT=${KUBE_GCE_NODE_SERVICE_ACCOUNT:-default} # KUBELET_TEST_ARGS are extra arguments passed to kubelet. @@ -109,8 +110,8 @@ export LOAD_IMAGE_COMMAND=${KUBE_LOAD_IMAGE_COMMAND:-ctr -n=k8s.io images import # if KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION or KUBE_UBUNTU_INSTALL_RUNC_VERSION # is set to empty then we do not override the version(s) and just # use whatever is in the default installation of containerd package -export UBUNTU_INSTALL_CONTAINERD_VERSION=${KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION:-} -export UBUNTU_INSTALL_RUNC_VERSION=${KUBE_UBUNTU_INSTALL_RUNC_VERSION:-} +export UBUNTU_INSTALL_CONTAINERD_VERSION=${KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION:-v2.1.4} +export UBUNTU_INSTALL_RUNC_VERSION=${KUBE_UBUNTU_INSTALL_RUNC_VERSION:-v1.3.2} # Ability to inject custom versions (COS images ONLY) # if KUBE_COS_INSTALL_CONTAINERD_VERSION or KUBE_COS_INSTALL_RUNC_VERSION diff --git a/cluster/gce/config-test.sh b/cluster/gce/config-test.sh index 983898458478b..36bb207d6a47a 100755 --- a/cluster/gce/config-test.sh +++ b/cluster/gce/config-test.sh @@ -101,13 +101,14 @@ ALLOWED_NOTREADY_NODES=${ALLOWED_NOTREADY_NODES:-$(($(get-num-nodes) / 100))} # By default, the latest image from the image family will be used unless an # explicit image will be set. GCI_VERSION=${KUBE_GCI_VERSION:-} -IMAGE_FAMILY=${KUBE_IMAGE_FAMILY:-cos-121-lts} +IMAGE_FAMILY=${KUBE_GCE_IMAGE_FAMILY:-cos-121-lts} +IMAGE_PROJECT=${KUBE_GCE_IMAGE_PROJECT:-cos-cloud} export MASTER_IMAGE=${KUBE_GCE_MASTER_IMAGE:-} export MASTER_IMAGE_FAMILY=${KUBE_GCE_MASTER_IMAGE_FAMILY:-${IMAGE_FAMILY}} -export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-cos-cloud} +export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-${IMAGE_PROJECT}} export NODE_IMAGE=${KUBE_GCE_NODE_IMAGE:-${GCI_VERSION}} export NODE_IMAGE_FAMILY=${KUBE_GCE_NODE_IMAGE_FAMILY:-${IMAGE_FAMILY}} -export NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-cos-cloud} +export NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-${IMAGE_PROJECT}} export NODE_SERVICE_ACCOUNT=${KUBE_GCE_NODE_SERVICE_ACCOUNT:-default} export CONTAINER_RUNTIME_ENDPOINT=${KUBE_CONTAINER_RUNTIME_ENDPOINT:-unix:///run/containerd/containerd.sock} @@ -122,8 +123,8 @@ export GCI_DOCKER_VERSION=${KUBE_GCI_DOCKER_VERSION:-} # if KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION or KUBE_UBUNTU_INSTALL_RUNC_VERSION # is set to empty then we do not override the version(s) and just # use whatever is in the default installation of containerd package -export UBUNTU_INSTALL_CONTAINERD_VERSION=${KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION:-} -export UBUNTU_INSTALL_RUNC_VERSION=${KUBE_UBUNTU_INSTALL_RUNC_VERSION:-} +export UBUNTU_INSTALL_CONTAINERD_VERSION=${KUBE_UBUNTU_INSTALL_CONTAINERD_VERSION:-v2.1.4} +export UBUNTU_INSTALL_RUNC_VERSION=${KUBE_UBUNTU_INSTALL_RUNC_VERSION:-v1.3.2} # Ability to inject custom versions (COS images ONLY) # if KUBE_COS_INSTALL_CONTAINERD_VERSION or KUBE_COS_INSTALL_RUNC_VERSION diff --git a/cluster/gce/gci/configure-helper.sh b/cluster/gce/gci/configure-helper.sh index c57e8d253b785..67df1482d9f2b 100755 --- a/cluster/gce/gci/configure-helper.sh +++ b/cluster/gce/gci/configure-helper.sh @@ -1984,6 +1984,7 @@ def resolve(host): container_security_context="\"securityContext\": {\"runAsUser\": ${ETCD_RUNASUSER}, \"runAsGroup\": ${ETCD_RUNASGROUP}, \"allowPrivilegeEscalation\": false, \"capabilities\": {\"drop\": [\"all\"]}}," fi sed -i -e "s@{{security_context}}@${container_security_context}@g" "${temp_file}" + mv "${temp_file}" /etc/kubernetes/manifests } diff --git a/cluster/gce/gci/configure.sh b/cluster/gce/gci/configure.sh index ab7082096148c..f6d6c1e8e8226 100644 --- a/cluster/gce/gci/configure.sh +++ b/cluster/gce/gci/configure.sh @@ -24,15 +24,15 @@ set -o nounset set -o pipefail ### Hardcoded constants -DEFAULT_CNI_VERSION='v1.7.1' +DEFAULT_CNI_VERSION='v1.8.0' # CNI HASH for amd64 sha512 -DEFAULT_CNI_HASH='22c1da53be05ecb8a1672c0a91b38f87d5218babeff0cd0f86e50fc127bf27970d977e748c7c8de765482ed353294b20a75392fb21885d6cae3da724b15c84d3' -DEFAULT_NPD_VERSION='v0.8.21' -DEFAULT_NPD_HASH_AMD64='2805ee1da97e06a4b209c198f21763e8687c499687f1b712e4e8e767a75aee4385c9d7fc54f0b190610ea693293a09eded6661ecf15af7af3e787475540fae71' -DEFAULT_NPD_HASH_ARM64='e2291bbb06d4e831267c2b2a316a668bdff36a502e89826a355cd7f3beeba804e62a2b59a8ec666f4e83042032a379aba9592bcbddf0d57c5a81b3bc1913517c' -DEFAULT_CRICTL_VERSION='v1.33.0' -DEFAULT_CRICTL_AMD64_SHA512='9efd084f249bd59d9196e41c6c3c272b07ecac7dadad1b811f07d65ef46047c2886a4c345e84c9bc3252c8ab3bfd5c1429a30c0b61b97b8a7e5b8afb5efb20ec' -DEFAULT_CRICTL_ARM64_SHA512='8e712080441d9ca3aa99070e55fc768d2050b6f343297286ecc1046281aa9c15191cf2ce5946728048871736fb6ae928f58c10fb1282689264d733fa0029f332' +DEFAULT_CNI_HASH='a2696f937b3433eee4a0de44a0994190166b108f2b29adf9f7005fa4fbfff56e78cbe8a2fe2607d99d4bcc0d4a8183e0c27fa748c2f8fb08ae40b62f198fd45b' +DEFAULT_NPD_VERSION='v1.34.0' +DEFAULT_NPD_HASH_AMD64='3c55ff6ffadd77dbc3df3774d13164587103ca87c8b6914f5c71c87d8f498b78621e0c96538bb3c69f8f1b4194a6da553aa56b1b52001a7d9a67776ac24e80bd' +DEFAULT_NPD_HASH_ARM64='ca1d34e64b80f6b2bdf86cfde95154122d6e14c707a748ea6fc414a55f391b1bb572a96b6b2c285996af0232917fa87e14e037125aa03a62247383af3e48c095' +DEFAULT_CRICTL_VERSION='v1.34.0' +DEFAULT_CRICTL_AMD64_SHA512='6b5669fe6c0dbcb8d0e0910529a4559e22154ef7f524fa15f3e13dfced6bea2c90a531d99786ac8b24fb4cc9ead1ef294387b52a230ba6fdf83278ab9dbd6133' +DEFAULT_CRICTL_ARM64_SHA512='b2daa7f6b559cd32da6d3bcb82b356561c0bc2ffcf7dc5084547fbae6cb8570a96cf01c9bfaa6d868cf92d1c1fbbced2a32bf7e0328f62c420c180a86314278d' DEFAULT_MOUNTER_TAR_SHA='7956fd42523de6b3107ddc3ce0e75233d2fcb78436ff07a1389b6eaac91fb2b1b72a08f7a219eaf96ba1ca4da8d45271002e0d60e0644e796c665f99bb356516' AUTH_PROVIDER_GCP_HASH_LINUX_AMD64="${AUTH_PROVIDER_GCP_HASH_LINUX_AMD64:-156058e5b3994cba91c23831774033e0d505d6d8b80f43541ef6af91b320fd9dfaabe42ec8a8887b51d87104c2b57e1eb895649d681575ffc80dd9aee8e563db}" AUTH_PROVIDER_GCP_HASH_LINUX_ARM64="${AUTH_PROVIDER_GCP_HASH_LINUX_ARM64:-1aa3b0bea10a9755231989ffc150cbfa770f1d96932db7535473f7bfeb1108bafdae80202ae738d59495982512e716ff7366d5f414d0e76dd50519f98611f9ab}" @@ -225,6 +225,36 @@ function download-or-bust { done } +# Robust download with retry and exponential backoff +function download-robust { + local description="$1" dest_dir="$2" url="$3" + + local max_attempts=5 attempt=1 delay=5 + local file="${url##*/}" + local temp_file="${dest_dir}/.${file}.tmp" + + while [[ ${attempt} -le ${max_attempts} ]]; do + rm -f "${temp_file}" + + if curl --fail --location --silent --show-error --connect-timeout 20 --max-time 900 \ + --retry 3 --retry-delay 2 -o "${temp_file}" "${url}"; then + mv "${temp_file}" "${dest_dir}/${file}" + echo "== Downloaded ${description} from ${url}" + return 0 + fi + rm -f "${temp_file}" + + if [[ ${attempt} -lt ${max_attempts} ]]; then + sleep "${delay}" + delay=$((delay * 2)) + fi + ((attempt++)) + done + + echo "ERROR: Failed to download ${description} after ${max_attempts} attempts from ${url}" + return 1 +} + function is-preloaded { local -r key=$1 local -r value=$2 @@ -254,7 +284,7 @@ function install-gci-mounter-tools { mkdir -p "${CONTAINERIZED_MOUNTER_HOME}" chmod a+x "${CONTAINERIZED_MOUNTER_HOME}" mkdir -p "${CONTAINERIZED_MOUNTER_HOME}/rootfs" - download-or-bust "${mounter_tar_sha}" "https://storage.googleapis.com/kubernetes-release/gci-mounter/mounter.tar" + download-or-bust "${mounter_tar_sha}" "https://dl.k8s.io/gci-mounter/mounter.tar" cp "${KUBE_HOME}/kubernetes/server/bin/mounter" "${CONTAINERIZED_MOUNTER_HOME}/mounter" chmod a+x "${CONTAINERIZED_MOUNTER_HOME}/mounter" mv "${KUBE_HOME}/mounter.tar" /tmp/mounter.tar @@ -498,6 +528,7 @@ function install-containerd-ubuntu { socat \ curl \ gnupg2 \ + nfs-common \ software-properties-common \ lsb-release @@ -521,22 +552,26 @@ function install-containerd-ubuntu { # Override to latest versions of containerd and runc systemctl stop containerd if [[ -n "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}" ]]; then - # containerd versions have slightly different url(s), so try both - # shellcheck disable=SC2086 - ( curl ${CURL_FLAGS} \ - --location \ - "https://github.com/containerd/containerd/releases/download/${UBUNTU_INSTALL_CONTAINERD_VERSION}/containerd-${UBUNTU_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" \ - || curl ${CURL_FLAGS} \ - --location \ - "https://github.com/containerd/containerd/releases/download/${UBUNTU_INSTALL_CONTAINERD_VERSION}/containerd-${UBUNTU_INSTALL_CONTAINERD_VERSION:1}.${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" ) \ - | tar --overwrite -xzv -C /usr/ + local temp_dir + temp_dir=$(mktemp -d) + + # Download containerd + if download-robust "containerd ${UBUNTU_INSTALL_CONTAINERD_VERSION}" "${temp_dir}" \ + "https://github.com/containerd/containerd/releases/download/${UBUNTU_INSTALL_CONTAINERD_VERSION}/containerd-${UBUNTU_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz"; then + tar --overwrite -xzv -C /usr/ -f "${temp_dir}"/containerd-*.tar.gz + fi + rm -rf "${temp_dir}" fi if [[ -n "${UBUNTU_INSTALL_RUNC_VERSION:-}" ]]; then - # shellcheck disable=SC2086 - curl ${CURL_FLAGS} \ - --location \ - "https://github.com/opencontainers/runc/releases/download/${UBUNTU_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}" --output /usr/sbin/runc \ - && chmod 755 /usr/sbin/runc + local temp_dir + temp_dir=$(mktemp -d) + + # Download and install runc + if download-robust "runc ${UBUNTU_INSTALL_RUNC_VERSION}" "${temp_dir}" \ + "https://github.com/opencontainers/runc/releases/download/${UBUNTU_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}"; then + cp "${temp_dir}/runc.${HOST_ARCH}" /usr/sbin/runc && chmod 755 /usr/sbin/runc + fi + rm -rf "${temp_dir}" fi sudo systemctl start containerd } @@ -555,27 +590,30 @@ function install-containerd-cos { mount --bind /home/containerd /home/containerd mount -o remount,exec /home/containerd if [[ -n "${COS_INSTALL_CONTAINERD_VERSION:-}" ]]; then - # containerd versions have slightly different url(s), so try both - # shellcheck disable=SC2086 - ( curl ${CURL_FLAGS} \ - --location \ - "https://github.com/containerd/containerd/releases/download/${COS_INSTALL_CONTAINERD_VERSION}/containerd-${COS_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" \ - || curl ${CURL_FLAGS} \ - --location \ - "https://github.com/containerd/containerd/releases/download/${COS_INSTALL_CONTAINERD_VERSION}/containerd-${COS_INSTALL_CONTAINERD_VERSION:1}.${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" ) \ - | tar --overwrite -xzv -C /home/containerd/ - cp /usr/lib/systemd/system/containerd.service /etc/systemd/system/containerd.service - # fix the path of the new containerd binary - sed -i 's|ExecStart=.*|ExecStart=/home/containerd/bin/containerd|' /etc/systemd/system/containerd.service + local temp_dir + temp_dir=$(mktemp -d) + + # Download containerd + if download-robust "containerd ${COS_INSTALL_CONTAINERD_VERSION}" "${temp_dir}" \ + "https://github.com/containerd/containerd/releases/download/${COS_INSTALL_CONTAINERD_VERSION}/containerd-${COS_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz"; then + tar --overwrite -xzv -C /home/containerd/ -f "${temp_dir}"/containerd-*.tar.gz + cp /usr/lib/systemd/system/containerd.service /etc/systemd/system/containerd.service + sed -i 's|ExecStart=.*|ExecStart=/home/containerd/bin/containerd|' /etc/systemd/system/containerd.service + fi + rm -rf "${temp_dir}" fi if [[ -n "${COS_INSTALL_RUNC_VERSION:-}" ]]; then - # shellcheck disable=SC2086 - curl ${CURL_FLAGS} \ - --location \ - "https://github.com/opencontainers/runc/releases/download/${COS_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}" --output /home/containerd/bin/runc \ - && chmod 755 /home/containerd/bin/runc - # ensure runc gets picked up from the correct location - sed -i "/\[Service\]/a Environment=PATH=/home/containerd/bin:$PATH" /etc/systemd/system/containerd.service + local temp_dir + temp_dir=$(mktemp -d) + mkdir -p /home/containerd/bin + + # Download and install runc + if download-robust "runc ${COS_INSTALL_RUNC_VERSION}" "${temp_dir}" \ + "https://github.com/opencontainers/runc/releases/download/${COS_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}"; then + cp "${temp_dir}/runc.${HOST_ARCH}" /home/containerd/bin/runc && chmod 755 /home/containerd/bin/runc + sed -i "/\[Service\]/a Environment=PATH=/home/containerd/bin:$PATH" /etc/systemd/system/containerd.service + fi + rm -rf "${temp_dir}" fi systemctl daemon-reload sudo systemctl start containerd @@ -623,7 +661,7 @@ EOF function ensure-containerd-runtime { # Install containerd/runc if requested - if [[ -n "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}" || -n "${UBUNTU_INSTALL_RUNC_VERSION:-}" ]]; then + if [[ ( -n "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}" || -n "${UBUNTU_INSTALL_RUNC_VERSION:-}" ) && "$(lsb_release -si)" == "Ubuntu" ]]; then log-wrap "InstallContainerdUbuntu" install-containerd-ubuntu fi if [[ -n "${COS_INSTALL_CONTAINERD_VERSION:-}" || -n "${COS_INSTALL_RUNC_VERSION:-}" ]]; then diff --git a/cluster/gce/manifests/etcd.manifest b/cluster/gce/manifests/etcd.manifest index 02f6daf45b1ef..b73ee24a91fd4 100644 --- a/cluster/gce/manifests/etcd.manifest +++ b/cluster/gce/manifests/etcd.manifest @@ -18,7 +18,7 @@ { "name": "etcd-container", {{security_context}} - "image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.6.5-0') }}", + "image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.6.6-0') }}", "resources": { "requests": { "cpu": {{ cpulimit }} @@ -43,7 +43,7 @@ "value": "{{ pillar.get('storage_backend', 'etcd3') }}" }, { "name": "TARGET_VERSION", - "value": "{{ pillar.get('etcd_version', '3.6.5') }}" + "value": "{{ pillar.get('etcd_version', '3.6.6') }}" }, { "name": "DO_NOT_MOVE_BINARIES", diff --git a/cluster/gce/manifests/kube-addon-manager.yaml b/cluster/gce/manifests/kube-addon-manager.yaml index bc943e80917d0..ed17b59282534 100644 --- a/cluster/gce/manifests/kube-addon-manager.yaml +++ b/cluster/gce/manifests/kube-addon-manager.yaml @@ -23,7 +23,7 @@ spec: - all # When updating version also bump it in: # - test/kubemark/resources/manifests/kube-addon-manager.yaml - image: registry.k8s.io/addon-manager/kube-addon-manager:v9.1.7 + image: registry.k8s.io/addon-manager/kube-addon-manager:v9.1.8 command: - /bin/bash - -c diff --git a/cluster/gce/upgrade-aliases.sh b/cluster/gce/upgrade-aliases.sh index fb88cd80cf652..b8f2f4648c7d3 100755 --- a/cluster/gce/upgrade-aliases.sh +++ b/cluster/gce/upgrade-aliases.sh @@ -170,8 +170,8 @@ export KUBE_GCE_ENABLE_IP_ALIASES=true export SECONDARY_RANGE_NAME="pods-default" export STORAGE_BACKEND="etcd3" export STORAGE_MEDIA_TYPE="application/vnd.kubernetes.protobuf" -export ETCD_IMAGE=3.6.5-0 -export ETCD_VERSION=3.6.5 +export ETCD_IMAGE=3.6.6-0 +export ETCD_VERSION=3.6.6 # Upgrade master with updated kube envs "${KUBE_ROOT}/cluster/gce/upgrade.sh" -M -l diff --git a/cluster/gce/util.sh b/cluster/gce/util.sh index 98f2002aac141..cbc361b0c7fcd 100755 --- a/cluster/gce/util.sh +++ b/cluster/gce/util.sh @@ -54,54 +54,36 @@ if [[ ${NODE_LOCAL_SSDS:-} -ge 1 ]] && [[ -n ${NODE_LOCAL_SSDS_EXT:-} ]] ; then exit 2 fi -if [[ "${MASTER_OS_DISTRIBUTION}" == "gci" ]]; then - DEFAULT_GCI_PROJECT=google-containers - if [[ "${GCI_VERSION}" == "cos"* ]] || [[ "${MASTER_IMAGE_FAMILY}" == "cos"* ]]; then - DEFAULT_GCI_PROJECT=cos-cloud - fi - export MASTER_IMAGE_PROJECT=${KUBE_GCE_MASTER_PROJECT:-${DEFAULT_GCI_PROJECT}} +# If the master image is not set, we use the latest image based on image +# family. +kube_master_image="${KUBE_GCE_MASTER_IMAGE:-}" +if [[ -z "${kube_master_image}" ]]; then + # remove architecture:X86_64 once we move on from ubuntu jammy as that image family has both X86_64 and ARM images + kube_master_image=$(gcloud compute images list --project="${MASTER_IMAGE_PROJECT}" --no-standard-images --filter="family:${MASTER_IMAGE_FAMILY} architecture:X86_64" --format 'value(name)') +fi - # If the master image is not set, we use the latest image based on image - # family. - kube_master_image="${KUBE_GCE_MASTER_IMAGE:-${GCI_VERSION}}" - if [[ -z "${kube_master_image}" ]]; then - kube_master_image=$(gcloud compute images list --project="${MASTER_IMAGE_PROJECT}" --no-standard-images --filter="family:${MASTER_IMAGE_FAMILY}" --format 'value(name)') - fi +echo "Using image: ${kube_master_image} from project: ${MASTER_IMAGE_PROJECT} as master image" >&2 +export MASTER_IMAGE="${kube_master_image}" - echo "Using image: ${kube_master_image} from project: ${MASTER_IMAGE_PROJECT} as master image" >&2 - export MASTER_IMAGE="${kube_master_image}" -fi # Sets node image based on the specified os distro. Currently this function only # supports gci and debian. # -# Requires: -# NODE_OS_DISTRIBUTION # Sets: -# DEFAULT_GCI_PROJECT # NODE_IMAGE -# NODE_IMAGE_PROJECT function set-linux-node-image() { - if [[ "${NODE_OS_DISTRIBUTION}" == "gci" ]]; then - DEFAULT_GCI_PROJECT=google-containers - if [[ "${GCI_VERSION}" == "cos"* ]] || [[ "${NODE_IMAGE_FAMILY}" == "cos"* ]]; then - DEFAULT_GCI_PROJECT=cos-cloud - fi - - # If the node image is not set, we use the latest image based on image - # family. - # Otherwise, we respect whatever is set by the user. - NODE_IMAGE_PROJECT=${KUBE_GCE_NODE_PROJECT:-${DEFAULT_GCI_PROJECT}} - local kube_node_image - kube_node_image="${KUBE_GCE_NODE_IMAGE:-${GCI_VERSION}}" - if [[ -z "${kube_node_image}" ]]; then - kube_node_image=$(gcloud compute images list --project="${NODE_IMAGE_PROJECT}" --no-standard-images --filter="family:${NODE_IMAGE_FAMILY}" --format 'value(name)') - fi + # If the node image is not set, we use the latest image based on image + # family. - echo "Using image: ${kube_node_image} from project: ${NODE_IMAGE_PROJECT} as node image" >&2 - export NODE_IMAGE="${kube_node_image}" + kube_node_image="${KUBE_GCE_NODE_IMAGE:-}" + if [[ -z "${kube_node_image}" ]]; then + # remove architecture:X86_64 once we move on from ubuntu jammy as that image family has both X86_64 and ARM images + kube_node_image=$(gcloud compute images list --project="${NODE_IMAGE_PROJECT}" --no-standard-images --filter="family:${NODE_IMAGE_FAMILY} architecture:X86_64" --format 'value(name)') fi + + echo "Using image: ${kube_node_image} from project: ${NODE_IMAGE_PROJECT} as node image" >&2 + export NODE_IMAGE="${kube_node_image}" } # Requires: diff --git a/cluster/gce/windows/k8s-node-setup.psm1 b/cluster/gce/windows/k8s-node-setup.psm1 index 402c75d1cb698..af23449eaadf3 100644 --- a/cluster/gce/windows/k8s-node-setup.psm1 +++ b/cluster/gce/windows/k8s-node-setup.psm1 @@ -57,8 +57,8 @@ $GCE_METADATA_SERVER = "169.254.169.254" # exist until an initial HNS network has been created on the Windows node - see # Add_InitialHnsNetwork(). $MGMT_ADAPTER_NAME = "vEthernet (Ethernet*" -$CRICTL_VERSION = 'v1.33.0' -$CRICTL_SHA512 = '81c91c76bb9059837e62b33b7df9f1503013a30525fceb248ca379b57c7ad8ba043dbb0432870224ebc42853c740caa422ddd62989b4a58829b66505f8e11969' +$CRICTL_VERSION = 'v1.34.0' +$CRICTL_SHA512 = 'b062756922dc5c5f41c5b13922dfea379cb9f4ce2431d1676c7dbace4f45c28a99ee71fd6cdd6c6f3d7679d33ac0ff052208207fc3f341b4ad047512dad756cb' Import-Module -Force C:\common.psm1 @@ -1013,7 +1013,6 @@ function Start-WorkerServices { # otherwise kubelet and kube-proxy will not be able to run properly. $instance_name = "$(Get-InstanceMetadata 'name' | Out-String)" $default_kubelet_args = @(` - "--pod-infra-container-image=${env:INFRA_CONTAINER}", "--hostname-override=${instance_name}" ) diff --git a/cluster/images/etcd-version-monitor/etcd-version-monitor.go b/cluster/images/etcd-version-monitor/etcd-version-monitor.go index 5dea78eef371b..55ad6fcc17c3b 100644 --- a/cluster/images/etcd-version-monitor/etcd-version-monitor.go +++ b/cluster/images/etcd-version-monitor/etcd-version-monitor.go @@ -25,9 +25,9 @@ import ( "net/http" "time" - "github.com/gogo/protobuf/proto" dto "github.com/prometheus/client_model/go" "github.com/spf13/pflag" + "google.golang.org/protobuf/proto" "k8s.io/component-base/metrics" "k8s.io/component-base/metrics/testutil" diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index 848f415b37f23..00f302e8e9b36 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -31,29 +31,30 @@ import ( var apiVersionPriorities = merge(controlplaneapiserver.DefaultGenericAPIServicePriorities(), map[schema.GroupVersion]controlplaneapiserver.APIServicePriority{ {Group: "", Version: "v1"}: {Group: 18000, Version: 1}, // to my knowledge, nothing below here collides - {Group: "apps", Version: "v1"}: {Group: 17800, Version: 15}, - {Group: "autoscaling", Version: "v1"}: {Group: 17500, Version: 15}, - {Group: "autoscaling", Version: "v2"}: {Group: 17500, Version: 30}, - {Group: "autoscaling", Version: "v2beta1"}: {Group: 17500, Version: 9}, - {Group: "autoscaling", Version: "v2beta2"}: {Group: 17500, Version: 1}, - {Group: "batch", Version: "v1"}: {Group: 17400, Version: 15}, - {Group: "batch", Version: "v1beta1"}: {Group: 17400, Version: 9}, - {Group: "batch", Version: "v2alpha1"}: {Group: 17400, Version: 9}, - {Group: "networking.k8s.io", Version: "v1"}: {Group: 17200, Version: 15}, - {Group: "networking.k8s.io", Version: "v1beta1"}: {Group: 17200, Version: 9}, - {Group: "policy", Version: "v1"}: {Group: 17100, Version: 15}, - {Group: "policy", Version: "v1beta1"}: {Group: 17100, Version: 9}, - {Group: "storage.k8s.io", Version: "v1"}: {Group: 16800, Version: 15}, - {Group: "storage.k8s.io", Version: "v1beta1"}: {Group: 16800, Version: 9}, - {Group: "storage.k8s.io", Version: "v1alpha1"}: {Group: 16800, Version: 1}, - {Group: "scheduling.k8s.io", Version: "v1"}: {Group: 16600, Version: 15}, - {Group: "node.k8s.io", Version: "v1"}: {Group: 16300, Version: 15}, - {Group: "node.k8s.io", Version: "v1alpha1"}: {Group: 16300, Version: 1}, - {Group: "node.k8s.io", Version: "v1beta1"}: {Group: 16300, Version: 9}, - {Group: "resource.k8s.io", Version: "v1"}: {Group: 16200, Version: 21}, - {Group: "resource.k8s.io", Version: "v1beta2"}: {Group: 16200, Version: 15}, - {Group: "resource.k8s.io", Version: "v1beta1"}: {Group: 16200, Version: 9}, - {Group: "resource.k8s.io", Version: "v1alpha3"}: {Group: 16200, Version: 1}, + {Group: "apps", Version: "v1"}: {Group: 17800, Version: 15}, + {Group: "autoscaling", Version: "v1"}: {Group: 17500, Version: 15}, + {Group: "autoscaling", Version: "v2"}: {Group: 17500, Version: 30}, + {Group: "autoscaling", Version: "v2beta1"}: {Group: 17500, Version: 9}, + {Group: "autoscaling", Version: "v2beta2"}: {Group: 17500, Version: 1}, + {Group: "batch", Version: "v1"}: {Group: 17400, Version: 15}, + {Group: "batch", Version: "v1beta1"}: {Group: 17400, Version: 9}, + {Group: "batch", Version: "v2alpha1"}: {Group: 17400, Version: 9}, + {Group: "networking.k8s.io", Version: "v1"}: {Group: 17200, Version: 15}, + {Group: "networking.k8s.io", Version: "v1beta1"}: {Group: 17200, Version: 9}, + {Group: "policy", Version: "v1"}: {Group: 17100, Version: 15}, + {Group: "policy", Version: "v1beta1"}: {Group: 17100, Version: 9}, + {Group: "storage.k8s.io", Version: "v1"}: {Group: 16800, Version: 15}, + {Group: "storage.k8s.io", Version: "v1beta1"}: {Group: 16800, Version: 9}, + {Group: "storage.k8s.io", Version: "v1alpha1"}: {Group: 16800, Version: 1}, + {Group: "scheduling.k8s.io", Version: "v1"}: {Group: 16600, Version: 15}, + {Group: "scheduling.k8s.io", Version: "v1alpha1"}: {Group: 16600, Version: 1}, + {Group: "node.k8s.io", Version: "v1"}: {Group: 16300, Version: 15}, + {Group: "node.k8s.io", Version: "v1alpha1"}: {Group: 16300, Version: 1}, + {Group: "node.k8s.io", Version: "v1beta1"}: {Group: 16300, Version: 9}, + {Group: "resource.k8s.io", Version: "v1"}: {Group: 16200, Version: 21}, + {Group: "resource.k8s.io", Version: "v1beta2"}: {Group: 16200, Version: 15}, + {Group: "resource.k8s.io", Version: "v1beta1"}: {Group: 16200, Version: 9}, + {Group: "resource.k8s.io", Version: "v1alpha3"}: {Group: 16200, Version: 1}, // Append a new group to the end of the list if unsure. // You can use min(existing group)-100 as the initial value for a group. // Version can be set to 9 (to have space around) for a new group. diff --git a/cmd/kube-apiserver/app/options/options_test.go b/cmd/kube-apiserver/app/options/options_test.go index 629bb3e00050c..da9063841604c 100644 --- a/cmd/kube-apiserver/app/options/options_test.go +++ b/cmd/kube-apiserver/app/options/options_test.go @@ -128,6 +128,7 @@ func TestAddFlags(t *testing.T) { "--service-cluster-ip-range=192.168.128.0/17", "--lease-reuse-duration-seconds=100", "--emulated-version=test=1.31", + "--min-compatibility-version=test=1.29", "--emulation-forward-compatible=true", "--runtime-config-emulation-forward-compatible=true", "--coordinated-leadership-lease-duration=10s", @@ -358,4 +359,7 @@ func TestAddFlags(t *testing.T) { if testEffectiveVersion.EmulationVersion().String() != "1.31" { t.Errorf("Got emulation version %s, wanted %s", testEffectiveVersion.EmulationVersion().String(), "1.31") } + if testEffectiveVersion.MinCompatibilityVersion().String() != "1.29" { + t.Errorf("Got min compatibility version %s, wanted %s", testEffectiveVersion.MinCompatibilityVersion().String(), "1.29") + } } diff --git a/cmd/kube-apiserver/app/options/validation_test.go b/cmd/kube-apiserver/app/options/validation_test.go index 452c4ff016377..78dcd0a4e7b7a 100644 --- a/cmd/kube-apiserver/app/options/validation_test.go +++ b/cmd/kube-apiserver/app/options/validation_test.go @@ -187,8 +187,10 @@ func TestClusterServiceIPRange(t *testing.T) { if !tc.ipAllocatorGate { featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32")) } - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MultiCIDRServiceAllocator, tc.ipAllocatorGate) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DisableAllocatorDualWrite, tc.disableDualWriteGate) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.MultiCIDRServiceAllocator: tc.ipAllocatorGate, + features.DisableAllocatorDualWrite: tc.disableDualWriteGate, + }) errs := validateClusterIPFlags(tc.options.Extra) if len(errs) > 0 && !tc.expectErrors { diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 129c64ed0e436..7a7df4a6bd03b 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -38,6 +38,7 @@ import ( genericapifilters "k8s.io/apiserver/pkg/endpoints/filters" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/egressselector" + "k8s.io/apiserver/pkg/server/flagz" serverstorage "k8s.io/apiserver/pkg/server/storage" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/apiserver/pkg/util/notfoundhandler" @@ -55,7 +56,6 @@ import ( "k8s.io/component-base/term" utilversion "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" - "k8s.io/component-base/zpages/flagz" "k8s.io/klog/v2" aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver" "k8s.io/kubernetes/cmd/kube-apiserver/app/options" diff --git a/cmd/kube-apiserver/app/testing/testserver.go b/cmd/kube-apiserver/app/testing/testserver.go index f14823e3d093b..fd55cb1c6028e 100644 --- a/cmd/kube-apiserver/app/testing/testserver.go +++ b/cmd/kube-apiserver/app/testing/testserver.go @@ -45,6 +45,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" + genericfeatures "k8s.io/apiserver/pkg/features" + "k8s.io/apiserver/pkg/server/flagz" serveroptions "k8s.io/apiserver/pkg/server/options" "k8s.io/apiserver/pkg/storage/storagebackend" "k8s.io/apiserver/pkg/storageversion" @@ -56,13 +58,12 @@ import ( "k8s.io/client-go/util/cert" "k8s.io/client-go/util/keyutil" basecompatibility "k8s.io/component-base/compatibility" + "k8s.io/component-base/featuregate" featuregatetesting "k8s.io/component-base/featuregate/testing" logsapi "k8s.io/component-base/logs/api/v1" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" "k8s.io/klog/v2" "k8s.io/kube-aggregator/pkg/apiserver" - "k8s.io/kubernetes/pkg/features" testutil "k8s.io/kubernetes/test/utils" "k8s.io/kubernetes/test/utils/ktesting" @@ -198,6 +199,7 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions, effectiveVersion = basecompatibility.NewEffectiveVersionFromString(instanceOptions.BinaryVersion, "", "") } effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) + effectiveVersion.SetMinCompatibilityVersion(featureGate.MinCompatibilityVersion()) componentGlobalsRegistry := basecompatibility.NewComponentGlobalsRegistry() if err := componentGlobalsRegistry.Register(basecompatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { return result, err @@ -377,18 +379,22 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions, // If the local ComponentGlobalsRegistry is changed by the flags, // we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. // We cannot directly use DefaultFeatureGate in ComponentGlobalsRegistry because the changes done by ComponentGlobalsRegistry.Set() will not be undone at the end of the test. - if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) { - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion()) + if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) || !featureGate.MinCompatibilityVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.MinCompatibilityVersion()) { + featuregatetesting.SetFeatureGateVersionsDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion(), effectiveVersion.MinCompatibilityVersion()) } + featureOverrides := map[featuregate.Feature]bool{} for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, featureGate.Enabled(f)) + featureOverrides[f] = featureGate.Enabled(f) } } + if len(featureOverrides) > 0 { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featureOverrides) + } utilfeature.DefaultMutableFeatureGate.AddMetrics() if instanceOptions.EnableCertAuth { - if featureGate.Enabled(features.UnknownVersionInteroperabilityProxy) { + if featureGate.Enabled(genericfeatures.UnknownVersionInteroperabilityProxy) { // TODO: set up a general clean up for testserver if clientgotransport.DialerStopCh == wait.NeverStop { ctx, cancel := context.WithTimeout(context.Background(), time.Hour) diff --git a/cmd/kube-controller-manager/OWNERS b/cmd/kube-controller-manager/OWNERS index 11416b81d12d6..f7c449bbe6465 100644 --- a/cmd/kube-controller-manager/OWNERS +++ b/cmd/kube-controller-manager/OWNERS @@ -33,6 +33,7 @@ reviewers: - thockin - wojtek-t - jpbetz + - jefftree labels: - sig/api-machinery emeritus_approvers: diff --git a/cmd/kube-controller-manager/app/apps.go b/cmd/kube-controller-manager/app/apps.go index b1f84a480de21..4c01a5e1154fa 100644 --- a/cmd/kube-controller-manager/app/apps.go +++ b/cmd/kube-controller-manager/app/apps.go @@ -25,7 +25,6 @@ import ( "time" "k8s.io/client-go/util/flowcontrol" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/daemon" "k8s.io/kubernetes/pkg/controller/deployment" @@ -35,12 +34,18 @@ import ( func newDaemonSetControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.DaemonSetController, - aliases: []string{"daemonset"}, - initFunc: startDaemonSetController, + name: names.DaemonSetController, + aliases: []string{"daemonset"}, + constructor: newDaemonSetController, } } -func startDaemonSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { + +func newDaemonSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("daemon-set-controller") + if err != nil { + return nil, err + } + dsc, err := daemon.NewNodeSelectorAwareDaemonSetsController( ctx, controllerContext.OpenShiftContext.OpenShiftDefaultProjectNodeSelector, @@ -50,73 +55,97 @@ func startDaemonSetController(ctx context.Context, controllerContext ControllerC controllerContext.InformerFactory.Apps().V1().ControllerRevisions(), controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Nodes(), - controllerContext.ClientBuilder.ClientOrDie("daemon-set-controller"), + client, flowcontrol.NewBackOff(1*time.Second, 15*time.Minute), ) if err != nil { - return nil, true, fmt.Errorf("error creating DaemonSets controller: %v", err) + return nil, fmt.Errorf("error creating DaemonSets controller: %w", err) } - go dsc.Run(ctx, int(controllerContext.ComponentConfig.DaemonSetController.ConcurrentDaemonSetSyncs)) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + dsc.Run(ctx, int(controllerContext.ComponentConfig.DaemonSetController.ConcurrentDaemonSetSyncs)) + }, controllerName), nil } func newStatefulSetControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.StatefulSetController, - aliases: []string{"statefulset"}, - initFunc: startStatefulSetController, + name: names.StatefulSetController, + aliases: []string{"statefulset"}, + constructor: newStatefulSetController, } } -func startStatefulSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go statefulset.NewStatefulSetController( + +func newStatefulSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("statefulset-controller") + if err != nil { + return nil, err + } + + ssc := statefulset.NewStatefulSetController( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Apps().V1().StatefulSets(), controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), controllerContext.InformerFactory.Apps().V1().ControllerRevisions(), - controllerContext.ClientBuilder.ClientOrDie("statefulset-controller"), - ).Run(ctx, int(controllerContext.ComponentConfig.StatefulSetController.ConcurrentStatefulSetSyncs)) - return nil, true, nil + client, + ) + return newControllerLoop(func(ctx context.Context) { + ssc.Run(ctx, int(controllerContext.ComponentConfig.StatefulSetController.ConcurrentStatefulSetSyncs)) + }, controllerName), nil } func newReplicaSetControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ReplicaSetController, - aliases: []string{"replicaset"}, - initFunc: startReplicaSetController, + name: names.ReplicaSetController, + aliases: []string{"replicaset"}, + constructor: newReplicaSetController, } } -func startReplicaSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go replicaset.NewReplicaSetController( +func newReplicaSetController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("replicaset-controller") + if err != nil { + return nil, err + } + + rsc := replicaset.NewReplicaSetController( ctx, controllerContext.InformerFactory.Apps().V1().ReplicaSets(), controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.ClientBuilder.ClientOrDie("replicaset-controller"), + client, replicaset.BurstReplicas, - ).Run(ctx, int(controllerContext.ComponentConfig.ReplicaSetController.ConcurrentRSSyncs)) - return nil, true, nil + ) + return newControllerLoop(func(ctx context.Context) { + rsc.Run(ctx, int(controllerContext.ComponentConfig.ReplicaSetController.ConcurrentRSSyncs)) + }, controllerName), nil } func newDeploymentControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.DeploymentController, - aliases: []string{"deployment"}, - initFunc: startDeploymentController, + name: names.DeploymentController, + aliases: []string{"deployment"}, + constructor: newDeploymentController, } } -func startDeploymentController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newDeploymentController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("deployment-controller") + if err != nil { + return nil, err + } + dc, err := deployment.NewDeploymentController( ctx, controllerContext.InformerFactory.Apps().V1().Deployments(), controllerContext.InformerFactory.Apps().V1().ReplicaSets(), controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.ClientBuilder.ClientOrDie("deployment-controller"), + client, ) if err != nil { - return nil, true, fmt.Errorf("error creating Deployment controller: %v", err) + return nil, fmt.Errorf("error creating Deployment controller: %w", err) } - go dc.Run(ctx, int(controllerContext.ComponentConfig.DeploymentController.ConcurrentDeploymentSyncs)) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + dc.Run(ctx, int(controllerContext.ComponentConfig.DeploymentController.ConcurrentDeploymentSyncs)) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/autoscaling.go b/cmd/kube-controller-manager/app/autoscaling.go index ff0c6d60f30ea..f41592ffc1781 100644 --- a/cmd/kube-controller-manager/app/autoscaling.go +++ b/cmd/kube-controller-manager/app/autoscaling.go @@ -21,14 +21,12 @@ package app import ( "context" - + "fmt" "k8s.io/client-go/dynamic" "k8s.io/client-go/scale" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/podautoscaler" "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics" - resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1" "k8s.io/metrics/pkg/client/custom_metrics" "k8s.io/metrics/pkg/client/external_metrics" @@ -36,47 +34,50 @@ import ( func newHorizontalPodAutoscalerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.HorizontalPodAutoscalerController, - aliases: []string{"horizontalpodautoscaling"}, - initFunc: startHorizontalPodAutoscalerControllerWithRESTClient, + name: names.HorizontalPodAutoscalerController, + aliases: []string{"horizontalpodautoscaling"}, + constructor: newHorizontalPodAutoscalerController, } } -func startHorizontalPodAutoscalerControllerWithRESTClient(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - - clientConfig := controllerContext.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") - hpaClient := controllerContext.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") - - apiVersionsGetter := custom_metrics.NewAvailableAPIsGetter(hpaClient.Discovery()) - // invalidate the discovery information roughly once per resync interval our API - // information is *at most* two resync intervals old. - go custom_metrics.PeriodicallyInvalidate( - apiVersionsGetter, - controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerSyncPeriod.Duration, - ctx.Done()) - - metricsClient := metrics.NewRESTMetricsClient( - resourceclient.NewForConfigOrDie(clientConfig), - custom_metrics.NewForConfig(clientConfig, controllerContext.RESTMapper, apiVersionsGetter), - external_metrics.NewForConfigOrDie(clientConfig), - ) - return startHPAControllerWithMetricsClient(ctx, controllerContext, metricsClient) -} - -func startHPAControllerWithMetricsClient(ctx context.Context, controllerContext ControllerContext, metricsClient metrics.MetricsClient) (controller.Interface, bool, error) { +func newHorizontalPodAutoscalerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + clientConfig, err := controllerContext.NewClientConfig("horizontal-pod-autoscaler") + if err != nil { + return nil, err + } - hpaClient := controllerContext.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") - hpaClientConfig := controllerContext.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") + hpaClient, err := controllerContext.NewClient("horizontal-pod-autoscaler") + if err != nil { + return nil, err + } // we don't use cached discovery because DiscoveryScaleKindResolver does its own caching, // so we want to re-fetch every time when we actually ask for it scaleKindResolver := scale.NewDiscoveryScaleKindResolver(hpaClient.Discovery()) - scaleClient, err := scale.NewForConfig(hpaClientConfig, controllerContext.RESTMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) + scaleClient, err := scale.NewForConfig(clientConfig, controllerContext.RESTMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) + if err != nil { + return nil, fmt.Errorf("failed to init HPA scale client: %w", err) + } + + apiVersionsGetter := custom_metrics.NewAvailableAPIsGetter(hpaClient.Discovery()) + + resourceClient, err := resourceclient.NewForConfig(clientConfig) + if err != nil { + return nil, fmt.Errorf("failed to init the resource client for %s: %w", controllerName, err) + } + + externalMetricsClient, err := external_metrics.NewForConfig(clientConfig) if err != nil { - return nil, false, err + return nil, fmt.Errorf("failed to init the external metrics client for %s: %w", controllerName, err) } - go podautoscaler.NewHorizontalController( + metricsClient := metrics.NewRESTMetricsClient( + resourceClient, + custom_metrics.NewForConfig(clientConfig, controllerContext.RESTMapper, apiVersionsGetter), + externalMetricsClient, + ) + + pas := podautoscaler.NewHorizontalController( ctx, hpaClient.CoreV1(), scaleClient, @@ -90,6 +91,16 @@ func startHPAControllerWithMetricsClient(ctx context.Context, controllerContext controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerTolerance, controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerCPUInitializationPeriod.Duration, controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerInitialReadinessDelay.Duration, - ).Run(ctx, int(controllerContext.ComponentConfig.HPAController.ConcurrentHorizontalPodAutoscalerSyncs)) - return nil, true, nil + ) + return newControllerLoop(concurrentRun( + func(ctx context.Context) { + custom_metrics.PeriodicallyInvalidate( + apiVersionsGetter, + controllerContext.ComponentConfig.HPAController.HorizontalPodAutoscalerSyncPeriod.Duration, + ctx.Done()) + }, + func(ctx context.Context) { + pas.Run(ctx, int(controllerContext.ComponentConfig.HPAController.ConcurrentHorizontalPodAutoscalerSyncs)) + }, + ), controllerName), nil } diff --git a/cmd/kube-controller-manager/app/batch.go b/cmd/kube-controller-manager/app/batch.go index 159aebd82848f..a1ae796755c0c 100644 --- a/cmd/kube-controller-manager/app/batch.go +++ b/cmd/kube-controller-manager/app/batch.go @@ -23,7 +23,6 @@ import ( "context" "fmt" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/cronjob" "k8s.io/kubernetes/pkg/controller/job" @@ -31,43 +30,58 @@ import ( func newJobControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.JobController, - aliases: []string{"job"}, - initFunc: startJobController, + name: names.JobController, + aliases: []string{"job"}, + constructor: newJobController, } } -func startJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - jobController, err := job.NewController( +func newJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("job-controller") + if err != nil { + return nil, err + } + + jc, err := job.NewController( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Batch().V1().Jobs(), - controllerContext.ClientBuilder.ClientOrDie("job-controller"), + client, ) if err != nil { - return nil, true, fmt.Errorf("creating Job controller: %v", err) + return nil, fmt.Errorf("creating Job controller: %w", err) } - go jobController.Run(ctx, int(controllerContext.ComponentConfig.JobController.ConcurrentJobSyncs)) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + jc.Run(ctx, int(controllerContext.ComponentConfig.JobController.ConcurrentJobSyncs)) + }, controllerName), nil } func newCronJobControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.CronJobController, - aliases: []string{"cronjob"}, - initFunc: startCronJobController, + name: names.CronJobController, + aliases: []string{"cronjob"}, + constructor: newCronJobController, } } -func startCronJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - cj2c, err := cronjob.NewControllerV2(ctx, controllerContext.InformerFactory.Batch().V1().Jobs(), +func newCronJobController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("cronjob-controller") + if err != nil { + return nil, err + } + + cj2c, err := cronjob.NewControllerV2( + ctx, + controllerContext.InformerFactory.Batch().V1().Jobs(), controllerContext.InformerFactory.Batch().V1().CronJobs(), - controllerContext.ClientBuilder.ClientOrDie("cronjob-controller"), + client, ) if err != nil { - return nil, true, fmt.Errorf("creating CronJob controller V2: %v", err) + return nil, fmt.Errorf("creating CronJob controller V2: %w", err) } - go cj2c.Run(ctx, int(controllerContext.ComponentConfig.CronJobController.ConcurrentCronJobSyncs)) - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + cj2c.Run(ctx, int(controllerContext.ComponentConfig.CronJobController.ConcurrentCronJobSyncs)) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/bootstrap.go b/cmd/kube-controller-manager/app/bootstrap.go index aedaaf65aa03f..4a3aeb8ed441f 100644 --- a/cmd/kube-controller-manager/app/bootstrap.go +++ b/cmd/kube-controller-manager/app/bootstrap.go @@ -20,7 +20,6 @@ import ( "context" "fmt" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/bootstrap" ) @@ -29,41 +28,53 @@ func newBootstrapSignerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ name: names.BootstrapSignerController, aliases: []string{"bootstrapsigner"}, - initFunc: startBootstrapSignerController, + constructor: newBootstrapSignerController, isDisabledByDefault: true, } } -func startBootstrapSignerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { + +func newBootstrapSignerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("bootstrap-signer") + if err != nil { + return nil, err + } + bsc, err := bootstrap.NewSigner( - controllerContext.ClientBuilder.ClientOrDie("bootstrap-signer"), + client, controllerContext.InformerFactory.Core().V1().Secrets(), controllerContext.InformerFactory.Core().V1().ConfigMaps(), bootstrap.DefaultSignerOptions(), ) if err != nil { - return nil, true, fmt.Errorf("error creating BootstrapSigner controller: %v", err) + return nil, fmt.Errorf("error creating BootstrapSigner controller: %w", err) } - go bsc.Run(ctx) - return nil, true, nil + + return newControllerLoop(bsc.Run, controllerName), nil } func newTokenCleanerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ name: names.TokenCleanerController, aliases: []string{"tokencleaner"}, - initFunc: startTokenCleanerController, + constructor: newTokenCleanerController, isDisabledByDefault: true, } } -func startTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { + +func newTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("token-cleaner") + if err != nil { + return nil, err + } + tcc, err := bootstrap.NewTokenCleaner( - controllerContext.ClientBuilder.ClientOrDie("token-cleaner"), + client, controllerContext.InformerFactory.Core().V1().Secrets(), bootstrap.DefaultTokenCleanerOptions(), ) if err != nil { - return nil, true, fmt.Errorf("error creating TokenCleaner controller: %v", err) + return nil, fmt.Errorf("error creating TokenCleaner controller: %w", err) } - go tcc.Run(ctx) - return nil, true, nil + + return newControllerLoop(tcc.Run, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/certificates.go b/cmd/kube-controller-manager/app/certificates.go index 7b86633ea313e..a959afb081a60 100644 --- a/cmd/kube-controller-manager/app/certificates.go +++ b/cmd/kube-controller-manager/app/certificates.go @@ -29,10 +29,8 @@ import ( "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/server/dynamiccertificates" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes" "k8s.io/component-base/featuregate" - "k8s.io/controller-manager/controller" "k8s.io/klog/v2" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/openshift-kube-controller-manager/servicecacertpublisher" @@ -48,33 +46,41 @@ import ( func newCertificateSigningRequestSigningControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.CertificateSigningRequestSigningController, - aliases: []string{"csrsigning"}, - initFunc: startCertificateSigningRequestSigningController, + name: names.CertificateSigningRequestSigningController, + aliases: []string{"csrsigning"}, + constructor: newCertificateSigningRequestSigningController, } } -func startCertificateSigningRequestSigningController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newCertificateSigningRequestSigningController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { logger := klog.FromContext(ctx) missingSingleSigningFile := controllerContext.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || controllerContext.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == "" if missingSingleSigningFile && !anySpecificFilesSet(controllerContext.ComponentConfig.CSRSigningController) { logger.Info("Skipping CSR signer controller because no csr cert/key was specified") - return nil, false, nil + return nil, nil } if !missingSingleSigningFile && anySpecificFilesSet(controllerContext.ComponentConfig.CSRSigningController) { - return nil, false, fmt.Errorf("cannot specify default and per controller certs at the same time") + return nil, fmt.Errorf("cannot specify default and per controller certs at the same time") + } + + c, err := controllerContext.NewClient("certificate-controller") + if err != nil { + return nil, err } - c := controllerContext.ClientBuilder.ClientOrDie("certificate-controller") csrInformer := controllerContext.InformerFactory.Certificates().V1().CertificateSigningRequests() certTTL := controllerContext.ComponentConfig.CSRSigningController.ClusterSigningDuration.Duration + var rx []runFunc if kubeletServingSignerCertFile, kubeletServingSignerKeyFile := getKubeletServingSignerFiles(controllerContext.ComponentConfig.CSRSigningController); len(kubeletServingSignerCertFile) > 0 || len(kubeletServingSignerKeyFile) > 0 { kubeletServingSigner, err := signer.NewKubeletServingCSRSigningController(ctx, c, csrInformer, kubeletServingSignerCertFile, kubeletServingSignerKeyFile, certTTL) if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kubelet-serving certificate controller: %v", err) + return nil, fmt.Errorf("failed to init kubernetes.io/kubelet-serving certificate controller: %w", err) } - go kubeletServingSigner.Run(ctx, 5) + + rx = append(rx, func(ctx context.Context) { + kubeletServingSigner.Run(ctx, 5) + }) } else { logger.Info("Skipping CSR signer controller because specific files were specified for other signers and not this one", "controller", "kubernetes.io/kubelet-serving") } @@ -82,9 +88,12 @@ func startCertificateSigningRequestSigningController(ctx context.Context, contro if kubeletClientSignerCertFile, kubeletClientSignerKeyFile := getKubeletClientSignerFiles(controllerContext.ComponentConfig.CSRSigningController); len(kubeletClientSignerCertFile) > 0 || len(kubeletClientSignerKeyFile) > 0 { kubeletClientSigner, err := signer.NewKubeletClientCSRSigningController(ctx, c, csrInformer, kubeletClientSignerCertFile, kubeletClientSignerKeyFile, certTTL) if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client-kubelet certificate controller: %v", err) + return nil, fmt.Errorf("failed to init kubernetes.io/kube-apiserver-client-kubelet certificate controller: %w", err) } - go kubeletClientSigner.Run(ctx, 5) + + rx = append(rx, func(ctx context.Context) { + kubeletClientSigner.Run(ctx, 5) + }) } else { logger.Info("Skipping CSR signer controller because specific files were specified for other signers and not this one", "controller", "kubernetes.io/kube-apiserver-client-kubelet") } @@ -92,9 +101,12 @@ func startCertificateSigningRequestSigningController(ctx context.Context, contro if kubeAPIServerSignerCertFile, kubeAPIServerSignerKeyFile := getKubeAPIServerClientSignerFiles(controllerContext.ComponentConfig.CSRSigningController); len(kubeAPIServerSignerCertFile) > 0 || len(kubeAPIServerSignerKeyFile) > 0 { kubeAPIServerClientSigner, err := signer.NewKubeAPIServerClientCSRSigningController(ctx, c, csrInformer, kubeAPIServerSignerCertFile, kubeAPIServerSignerKeyFile, certTTL) if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client certificate controller: %v", err) + return nil, fmt.Errorf("failed to init kubernetes.io/kube-apiserver-client certificate controller: %w", err) } - go kubeAPIServerClientSigner.Run(ctx, 5) + + rx = append(rx, func(ctx context.Context) { + kubeAPIServerClientSigner.Run(ctx, 5) + }) } else { logger.Info("Skipping CSR signer controller because specific files were specified for other signers and not this one", "controller", "kubernetes.io/kube-apiserver-client") } @@ -102,14 +114,17 @@ func startCertificateSigningRequestSigningController(ctx context.Context, contro if legacyUnknownSignerCertFile, legacyUnknownSignerKeyFile := getLegacyUnknownSignerFiles(controllerContext.ComponentConfig.CSRSigningController); len(legacyUnknownSignerCertFile) > 0 || len(legacyUnknownSignerKeyFile) > 0 { legacyUnknownSigner, err := signer.NewLegacyUnknownCSRSigningController(ctx, c, csrInformer, legacyUnknownSignerCertFile, legacyUnknownSignerKeyFile, certTTL) if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/legacy-unknown certificate controller: %v", err) + return nil, fmt.Errorf("failed to init kubernetes.io/legacy-unknown certificate controller: %w", err) } - go legacyUnknownSigner.Run(ctx, 5) + + rx = append(rx, func(ctx context.Context) { + legacyUnknownSigner.Run(ctx, 5) + }) } else { logger.Info("Skipping CSR signer controller because specific files were specified for other signers and not this one", "controller", "kubernetes.io/legacy-unknown") } - return nil, true, nil + return newControllerLoop(concurrentRun(rx...), controllerName), nil } func areKubeletServingSignerFilesSpecified(config csrsigningconfig.CSRSigningControllerConfiguration) bool { @@ -172,110 +187,130 @@ func getLegacyUnknownSignerFiles(config csrsigningconfig.CSRSigningControllerCon func newCertificateSigningRequestApprovingControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.CertificateSigningRequestApprovingController, - aliases: []string{"csrapproving"}, - initFunc: startCertificateSigningRequestApprovingController, + name: names.CertificateSigningRequestApprovingController, + aliases: []string{"csrapproving"}, + constructor: newCertificateSigningRequestApprovingController, } } -func startCertificateSigningRequestApprovingController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - approver := approver.NewCSRApprovingController( +func newCertificateSigningRequestApprovingController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("certificate-controller") + if err != nil { + return nil, err + } + + ac := approver.NewCSRApprovingController( ctx, - controllerContext.ClientBuilder.ClientOrDie("certificate-controller"), + client, controllerContext.InformerFactory.Certificates().V1().CertificateSigningRequests(), ) - go approver.Run(ctx, 5) - - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + ac.Run(ctx, 5) + }, controllerName), nil } func newCertificateSigningRequestCleanerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.CertificateSigningRequestCleanerController, - aliases: []string{"csrcleaner"}, - initFunc: startCertificateSigningRequestCleanerController, + name: names.CertificateSigningRequestCleanerController, + aliases: []string{"csrcleaner"}, + constructor: newCertificateSigningRequestCleanerController, } } -func startCertificateSigningRequestCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - cleaner := cleaner.NewCSRCleanerController( - controllerContext.ClientBuilder.ClientOrDie("certificate-controller").CertificatesV1().CertificateSigningRequests(), +func newCertificateSigningRequestCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("certificate-controller") + if err != nil { + return nil, err + } + + cc := cleaner.NewCSRCleanerController( + client.CertificatesV1().CertificateSigningRequests(), controllerContext.InformerFactory.Certificates().V1().CertificateSigningRequests(), ) - go cleaner.Run(ctx, 1) - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + cc.Run(ctx, 1) + }, controllerName), nil } func newPodCertificateRequestCleanerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PodCertificateRequestCleanerController, - initFunc: startPodCertificateRequestCleanerController, + name: names.PodCertificateRequestCleanerController, + constructor: newPodCertificateRequestCleanerController, requiredFeatureGates: []featuregate.Feature{ features.PodCertificateRequest, }, } } -func startPodCertificateRequestCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newPodCertificateRequestCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { cleaner := cleaner.NewPCRCleanerController( controllerContext.ClientBuilder.ClientOrDie("podcertificaterequestcleaner"), - controllerContext.InformerFactory.Certificates().V1alpha1().PodCertificateRequests(), + controllerContext.InformerFactory.Certificates().V1beta1().PodCertificateRequests(), clock.RealClock{}, 15*time.Minute, // We expect all PodCertificateRequest flows to complete faster than this. 5*time.Minute, ) - go cleaner.Run(ctx, 1) - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + cleaner.Run(ctx, 1) + }, controllerName), nil } func newRootCACertificatePublisherControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.RootCACertificatePublisherController, - aliases: []string{"root-ca-cert-publisher"}, - initFunc: startRootCACertificatePublisherController, + name: names.RootCACertificatePublisherController, + aliases: []string{"root-ca-cert-publisher"}, + constructor: newRootCACertificatePublisherController, } } -func startRootCACertificatePublisherController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newRootCACertificatePublisherController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { rootCA, err := getKubeAPIServerCAFileContents(controllerContext) if err != nil { - return nil, true, err + return nil, err + } + + client, err := controllerContext.NewClient("root-ca-cert-publisher") + if err != nil { + return nil, err } sac, err := rootcacertpublisher.NewPublisher( controllerContext.InformerFactory.Core().V1().ConfigMaps(), controllerContext.InformerFactory.Core().V1().Namespaces(), - controllerContext.ClientBuilder.ClientOrDie("root-ca-cert-publisher"), + client, rootCA, ) if err != nil { - return nil, true, fmt.Errorf("error creating root CA certificate publisher: %v", err) + return nil, fmt.Errorf("error creating root CA certificate publisher: %w", err) } - go sac.Run(ctx, 1) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + sac.Run(ctx, 1) + }, controllerName), nil } func newKubeAPIServerSignerClusterTrustBundledPublisherDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ name: names.KubeAPIServerClusterTrustBundlePublisherController, - initFunc: newKubeAPIServerSignerClusterTrustBundledPublisherController, + constructor: newKubeAPIServerSignerClusterTrustBundledPublisherController, requiredFeatureGates: []featuregate.Feature{features.ClusterTrustBundle}, } } type controllerConstructor func(string, dynamiccertificates.CAContentProvider, kubernetes.Interface) (ctbpublisher.PublisherRunner, error) -func newKubeAPIServerSignerClusterTrustBundledPublisherController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newKubeAPIServerSignerClusterTrustBundledPublisherController( + ctx context.Context, controllerContext ControllerContext, controllerName string, +) (Controller, error) { rootCA, err := getKubeAPIServerCAFileContents(controllerContext) if err != nil { - return nil, false, err + return nil, err } - - if len(rootCA) == 0 || !utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundle) { - return nil, false, nil + if len(rootCA) == 0 { + return nil, nil } servingSigners, err := dynamiccertificates.NewStaticCAContent("kube-apiserver-serving", rootCA) if err != nil { - return nil, false, fmt.Errorf("failed to create a static CA content provider for the kube-apiserver-serving signer: %w", err) + return nil, fmt.Errorf("failed to create a static CA content provider for the kube-apiserver-serving signer: %w", err) } schemaControllerMapping := map[schema.GroupVersion]controllerConstructor{ @@ -283,12 +318,16 @@ func newKubeAPIServerSignerClusterTrustBundledPublisherController(ctx context.Co certificatesv1beta1.SchemeGroupVersion: ctbpublisher.NewBetaClusterTrustBundlePublisher, } - apiserverSignerClient := controllerContext.ClientBuilder.ClientOrDie("kube-apiserver-serving-clustertrustbundle-publisher") + apiserverSignerClient, err := controllerContext.NewClient("kube-apiserver-serving-clustertrustbundle-publisher") + if err != nil { + return nil, err + } + var runner ctbpublisher.PublisherRunner for _, gv := range []schema.GroupVersion{certificatesv1beta1.SchemeGroupVersion, certificatesv1alpha1.SchemeGroupVersion} { ctbAvailable, err := clusterTrustBundlesAvailable(apiserverSignerClient, gv) if err != nil { - return nil, false, fmt.Errorf("discovery failed for ClusterTrustBundle: %w", err) + return nil, fmt.Errorf("discovery failed for ClusterTrustBundle: %w", err) } if !ctbAvailable { @@ -301,18 +340,17 @@ func newKubeAPIServerSignerClusterTrustBundledPublisherController(ctx context.Co apiserverSignerClient, ) if err != nil { - return nil, false, fmt.Errorf("error creating kube-apiserver-serving signer certificates publisher: %w", err) + return nil, fmt.Errorf("error creating kube-apiserver-serving signer certificates publisher: %w", err) } break } if runner == nil { klog.Info("no known scheme version was found for clustertrustbundles, cannot start kube-apiserver-serving-clustertrustbundle-publisher-controller") - return nil, false, nil + return nil, nil } - go runner.Run(ctx) - return nil, true, nil + return newControllerLoop(runner.Run, controllerName), nil } func clusterTrustBundlesAvailable(client kubernetes.Interface, schemaVersion schema.GroupVersion) (bool, error) { @@ -335,7 +373,11 @@ func clusterTrustBundlesAvailable(client kubernetes.Interface, schemaVersion sch func getKubeAPIServerCAFileContents(controllerContext ControllerContext) ([]byte, error) { if controllerContext.ComponentConfig.SAController.RootCAFile == "" { - return controllerContext.ClientBuilder.ConfigOrDie("root-ca-cert-publisher").CAData, nil + config, err := controllerContext.NewClientConfig("root-ca-cert-publisher") + if err != nil { + return nil, err + } + return config.CAData, nil } rootCA, err := readCA(controllerContext.ComponentConfig.SAController.RootCAFile) @@ -346,23 +388,24 @@ func getKubeAPIServerCAFileContents(controllerContext ControllerContext) ([]byte } -func newServiceCACertPublisher() *ControllerDescriptor { +func newServiceCACertPublisherControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ServiceCACertificatePublisherController, - aliases: []string{"service-ca-cert-publisher"}, - initFunc: startServiceCACertPublisher, + name: names.ServiceCACertificatePublisherController, + aliases: []string{"service-ca-cert-publisher"}, + constructor: newServiceCACertPublisherController, } } -func startServiceCACertPublisher(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newServiceCACertPublisherController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { sac, err := servicecacertpublisher.NewPublisher( controllerContext.InformerFactory.Core().V1().ConfigMaps(), controllerContext.InformerFactory.Core().V1().Namespaces(), controllerContext.ClientBuilder.ClientOrDie("service-ca-cert-publisher"), ) if err != nil { - return nil, true, fmt.Errorf("error creating service CA certificate publisher: %v", err) + return nil, fmt.Errorf("error creating service CA certificate publisher: %w", err) } - go sac.Run(1, ctx.Done()) - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + sac.Run(ctx, 1) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/config/config.go b/cmd/kube-controller-manager/app/config/config.go index 2e5b9ec78061e..56d09d2e54caf 100644 --- a/cmd/kube-controller-manager/app/config/config.go +++ b/cmd/kube-controller-manager/app/config/config.go @@ -18,12 +18,13 @@ package config import ( apiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/server/flagz" clientset "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" basecompatibility "k8s.io/component-base/compatibility" - "k8s.io/component-base/zpages/flagz" kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config" + "time" ) // Config is the main context object for the controller manager. @@ -35,8 +36,6 @@ type Config struct { ComponentConfig kubectrlmgrconfig.KubeControllerManagerConfiguration SecureServing *apiserver.SecureServingInfo - // LoopbackClientConfig is a config for a privileged loopback connection - LoopbackClientConfig *restclient.Config Authentication apiserver.AuthenticationInfo Authorization apiserver.AuthorizationInfo @@ -50,6 +49,8 @@ type Config struct { EventBroadcaster record.EventBroadcaster EventRecorder record.EventRecorder + ControllerShutdownTimeout time.Duration + // ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry } @@ -68,7 +69,5 @@ type CompletedConfig struct { func (c *Config) Complete() *CompletedConfig { cc := completedConfig{c} - apiserver.AuthorizeClientBearerToken(c.LoopbackClientConfig, &c.Authentication, &c.Authorization) - return &CompletedConfig{&cc} } diff --git a/cmd/kube-controller-manager/app/controller_descriptor.go b/cmd/kube-controller-manager/app/controller_descriptor.go new file mode 100644 index 0000000000000..30df1ee8aa65c --- /dev/null +++ b/cmd/kube-controller-manager/app/controller_descriptor.go @@ -0,0 +1,248 @@ +/* +Copyright 2025 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 app + +import ( + "context" + "fmt" + "sort" + + "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/component-base/featuregate" + "k8s.io/klog/v2" +) + +// This file contains types and functions for wrapping controller implementations from downstream packages. +// Every controller wrapper implements the Controller interface, +// which is then associated with a ControllerDescriptor, which holds additional static metadata +// needed so that the manager can manage Controllers properly. + +// Controller defines the base interface that all controller wrappers must implement. +type Controller interface { + // Name returns the controller's canonical name. + Name() string + + // Run runs the controller loop. + // When there is anything to be done, it blocks until the context is cancelled. + // Run must ensure all goroutines are terminated before returning. + Run(context.Context) +} + +// ControllerConstructor is a constructor for a controller. +// A nil Controller returned means that the associated controller is disabled. +type ControllerConstructor func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) + +type ControllerDescriptor struct { + name string + constructor ControllerConstructor + requiredFeatureGates []featuregate.Feature + aliases []string + isDisabledByDefault bool + isCloudProviderController bool + requiresSpecialHandling bool +} + +func (r *ControllerDescriptor) Name() string { + return r.name +} + +func (r *ControllerDescriptor) GetControllerConstructor() ControllerConstructor { + return r.constructor +} + +func (r *ControllerDescriptor) GetRequiredFeatureGates() []featuregate.Feature { + return append([]featuregate.Feature(nil), r.requiredFeatureGates...) +} + +// GetAliases returns aliases to ensure backwards compatibility and should never be removed! +// Only addition of new aliases is allowed, and only when a canonical name is changed (please see CHANGE POLICY of controller names) +func (r *ControllerDescriptor) GetAliases() []string { + return append([]string(nil), r.aliases...) +} + +func (r *ControllerDescriptor) IsDisabledByDefault() bool { + return r.isDisabledByDefault +} + +func (r *ControllerDescriptor) IsCloudProviderController() bool { + return r.isCloudProviderController +} + +// RequiresSpecialHandling should return true only in a special non-generic controllers like ServiceAccountTokenController +func (r *ControllerDescriptor) RequiresSpecialHandling() bool { + return r.requiresSpecialHandling +} + +// BuildController creates a controller based on the given descriptor. +// The associated controller's constructor is called at the end, so the same contract applies for the return values here. +func (r *ControllerDescriptor) BuildController(ctx context.Context, controllerCtx ControllerContext) (Controller, error) { + logger := klog.FromContext(ctx) + controllerName := r.Name() + + for _, featureGate := range r.GetRequiredFeatureGates() { + if !utilfeature.DefaultFeatureGate.Enabled(featureGate) { + logger.Info("Controller is disabled by a feature gate", + "controller", controllerName, + "requiredFeatureGates", r.GetRequiredFeatureGates()) + return nil, nil + } + } + + if r.IsCloudProviderController() { + logger.Info("Skipping a cloud provider controller", "controller", controllerName) + return nil, nil + } + + ctx = klog.NewContext(ctx, klog.LoggerWithName(logger, controllerName)) + return r.GetControllerConstructor()(ctx, controllerCtx, controllerName) +} + +// KnownControllers returns all known controllers' name +func KnownControllers() []string { + return sets.StringKeySet(NewControllerDescriptors()).List() +} + +// ControllerAliases returns a mapping of aliases to canonical controller names +func ControllerAliases() map[string]string { + aliases := map[string]string{} + for name, c := range NewControllerDescriptors() { + for _, alias := range c.GetAliases() { + aliases[alias] = name + } + } + return aliases +} + +func ControllersDisabledByDefault() []string { + var controllersDisabledByDefault []string + + for name, c := range NewControllerDescriptors() { + if c.IsDisabledByDefault() { + controllersDisabledByDefault = append(controllersDisabledByDefault, name) + } + } + + sort.Strings(controllersDisabledByDefault) + + return controllersDisabledByDefault +} + +// NewControllerDescriptors is a public map of named controller groups (you can start more than one in an init func) +// paired to their ControllerDescriptor wrapper object that includes the associated controller constructor. +// This allows for structured downstream composition and subdivision. +func NewControllerDescriptors() map[string]*ControllerDescriptor { + controllers := map[string]*ControllerDescriptor{} + aliases := sets.NewString() + + // All the controllers must fulfil common constraints, or else we will explode. + register := func(controllerDesc *ControllerDescriptor) { + if controllerDesc == nil { + panic("received nil controller for a registration") + } + name := controllerDesc.Name() + if len(name) == 0 { + panic("received controller without a name for a registration") + } + if _, found := controllers[name]; found { + panic(fmt.Sprintf("controller name %q was registered twice", name)) + } + if controllerDesc.GetControllerConstructor() == nil { + panic(fmt.Sprintf("controller %q does not have a constructor specified", name)) + } + + for _, alias := range controllerDesc.GetAliases() { + if aliases.Has(alias) { + panic(fmt.Sprintf("controller %q has a duplicate alias %q", name, alias)) + } + aliases.Insert(alias) + } + + controllers[name] = controllerDesc + } + + // First add "special" controllers that aren't initialized normally. These controllers cannot be initialized + // in the main controller loop initialization, so we add them here only for the metadata and duplication detection. + // app.ControllerDescriptor#RequiresSpecialHandling should return true for such controllers + // The only known special case is the ServiceAccountTokenController which *must* be started + // first to ensure that the SA tokens for future controllers will exist. Think very carefully before adding new + // special controllers. + register(newServiceAccountTokenControllerDescriptor(nil)) + + register(newEndpointsControllerDescriptor()) + register(newEndpointSliceControllerDescriptor()) + register(newEndpointSliceMirroringControllerDescriptor()) + register(newReplicationControllerDescriptor()) + register(newPodGarbageCollectorControllerDescriptor()) + register(newResourceQuotaControllerDescriptor()) + register(newNamespaceControllerDescriptor()) + register(newServiceAccountControllerDescriptor()) + register(newGarbageCollectorControllerDescriptor()) + register(newDaemonSetControllerDescriptor()) + register(newJobControllerDescriptor()) + register(newDeploymentControllerDescriptor()) + register(newReplicaSetControllerDescriptor()) + register(newHorizontalPodAutoscalerControllerDescriptor()) + register(newDisruptionControllerDescriptor()) + register(newStatefulSetControllerDescriptor()) + register(newCronJobControllerDescriptor()) + register(newCertificateSigningRequestSigningControllerDescriptor()) + register(newCertificateSigningRequestApprovingControllerDescriptor()) + register(newCertificateSigningRequestCleanerControllerDescriptor()) + register(newPodCertificateRequestCleanerControllerDescriptor()) + register(newTTLControllerDescriptor()) + register(newBootstrapSignerControllerDescriptor()) + register(newTokenCleanerControllerDescriptor()) + register(newNodeIpamControllerDescriptor()) + register(newNodeLifecycleControllerDescriptor()) + + register(newServiceLBControllerDescriptor()) // cloud provider controller + register(newNodeRouteControllerDescriptor()) // cloud provider controller + register(newCloudNodeLifecycleControllerDescriptor()) // cloud provider controller + + register(newPersistentVolumeBinderControllerDescriptor()) + register(newPersistentVolumeAttachDetachControllerDescriptor()) + register(newPersistentVolumeExpanderControllerDescriptor()) + register(newClusterRoleAggregrationControllerDescriptor()) + register(newPersistentVolumeClaimProtectionControllerDescriptor()) + register(newPersistentVolumeProtectionControllerDescriptor()) + register(newVolumeAttributesClassProtectionControllerDescriptor()) + register(newTTLAfterFinishedControllerDescriptor()) + register(newRootCACertificatePublisherControllerDescriptor()) + register(newKubeAPIServerSignerClusterTrustBundledPublisherDescriptor()) + register(newServiceCACertPublisherControllerDescriptor()) + register(newEphemeralVolumeControllerDescriptor()) + + // feature gated + register(newStorageVersionGarbageCollectorControllerDescriptor()) + register(newResourceClaimControllerDescriptor()) + register(newDeviceTaintEvictionControllerDescriptor()) + register(newLegacyServiceAccountTokenCleanerControllerDescriptor()) + register(newValidatingAdmissionPolicyStatusControllerDescriptor()) + register(newTaintEvictionControllerDescriptor()) + register(newServiceCIDRsControllerDescriptor()) + register(newStorageVersionMigratorControllerDescriptor()) + register(newSELinuxWarningControllerDescriptor()) + + for _, alias := range aliases.UnsortedList() { + if _, ok := controllers[alias]; ok { + panic(fmt.Sprintf("alias %q conflicts with a controller name", alias)) + } + } + + return controllers +} diff --git a/cmd/kube-controller-manager/app/controller_utils.go b/cmd/kube-controller-manager/app/controller_utils.go new file mode 100644 index 0000000000000..8a9ca1baa0e27 --- /dev/null +++ b/cmd/kube-controller-manager/app/controller_utils.go @@ -0,0 +1,67 @@ +/* +Copyright 2025 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 app + +import ( + "context" + "sync" +) + +// This file contains utility functions for implementing controller wrappers, +// i.e. turning whatever logic into a Controller. + +type runFunc func(ctx context.Context) + +type runFuncSlice []runFunc + +func (rx runFuncSlice) Run(ctx context.Context) { + var wg sync.WaitGroup + wg.Add(len(rx)) + for _, fnc := range rx { + go func() { + defer wg.Done() + fnc(ctx) + }() + } + wg.Wait() +} + +// concurrentRun returns a runFunc that wraps the given functions to run concurrently. +func concurrentRun(rx ...runFunc) runFunc { + return runFuncSlice(rx).Run +} + +// controllerLoop implements the Controller interface. It makes it easy to turn a function into a Controller. +type controllerLoop struct { + name string + run runFunc +} + +func newControllerLoop(run runFunc, controllerName string) *controllerLoop { + return &controllerLoop{ + name: controllerName, + run: run, + } +} + +func (loop *controllerLoop) Name() string { + return loop.name +} + +func (loop *controllerLoop) Run(ctx context.Context) { + loop.run(ctx) +} diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 76f30faf844ad..182e41b852c27 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -25,7 +25,7 @@ import ( "math/rand" "net/http" "os" - "sort" + "sync" "time" "github.com/blang/semver/v4" @@ -39,11 +39,14 @@ import ( "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/server/flagz" "k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/mux" + "k8s.io/apiserver/pkg/server/statusz" utilfeature "k8s.io/apiserver/pkg/util/feature" cacheddiscovery "k8s.io/client-go/discovery/cached/memory" "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" v1core "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/metadata" "k8s.io/client-go/metadata/metadatainformer" @@ -52,7 +55,6 @@ import ( "k8s.io/client-go/tools/leaderelection" "k8s.io/client-go/tools/leaderelection/resourcelock" certutil "k8s.io/client-go/util/cert" - "k8s.io/client-go/util/keyutil" cliflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/cli/globalflag" basecompatibility "k8s.io/component-base/compatibility" @@ -67,8 +69,6 @@ import ( utilversion "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" - "k8s.io/component-base/zpages/statusz" genericcontrollermanager "k8s.io/controller-manager/app" "k8s.io/controller-manager/controller" "k8s.io/controller-manager/pkg/clientbuilder" @@ -81,9 +81,7 @@ import ( "k8s.io/kubernetes/cmd/kube-controller-manager/names" kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config" garbagecollector "k8s.io/kubernetes/pkg/controller/garbagecollector" - serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" kubefeatures "k8s.io/kubernetes/pkg/features" - "k8s.io/kubernetes/pkg/serviceaccount" libgorestclient "github.com/openshift/library-go/pkg/config/client" ) @@ -250,7 +248,14 @@ func Run(ctx context.Context, c *config.CompletedConfig, stopCh2 <-chan struct{} } if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { - statusz.Install(unsecuredMux, kubeControllerManager, statusz.NewRegistry(c.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent))) + statusz.Install( + unsecuredMux, + kubeControllerManager, + statusz.NewRegistry( + c.ComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent), + statusz.WithListedPaths(unsecuredMux.ListedPaths()), + ), + ) } handler := genericcontrollermanager.BuildHandlerChain(unsecuredMux, &c.Authorization, &c.Authentication) @@ -271,16 +276,27 @@ func Run(ctx context.Context, c *config.CompletedConfig, stopCh2 <-chan struct{} klog.FlushAndExit(klog.ExitFlushTimeout, 1) } - if err := StartControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler); err != nil { - logger.Error(err, "Error starting controllers") + // Prepare all controllers in advance. + controllers, err := BuildControllers(ctx, controllerContext, controllerDescriptors, unsecuredMux, healthzHandler) + if err != nil { + logger.Error(err, "Error building controllers") klog.FlushAndExit(klog.ExitFlushTimeout, 1) } + // Start the informers. + stopCh := ctx.Done() controllerContext.InformerFactory.Start(stopCh) controllerContext.ObjectOrMetadataInformerFactory.Start(stopCh) close(controllerContext.InformersStarted) - <-ctx.Done() + // Actually start the controllers. + if len(controllers) > 0 { + if !RunControllers(ctx, controllerContext, controllers, ControllerStartJitter, c.ControllerShutdownTimeout) { + klog.FlushAndExit(klog.ExitFlushTimeout, 1) + } + } else { + <-ctx.Done() + } } // No leader election, run directly @@ -309,14 +325,23 @@ func Run(ctx context.Context, c *config.CompletedConfig, stopCh2 <-chan struct{} leaderMigrator = leadermigration.NewLeaderMigrator(&c.ComponentConfig.Generic.LeaderMigration, kubeControllerManager) - // startSATokenControllerInit is the original InitFunc. - startSATokenControllerInit := saTokenControllerDescriptor.GetInitFunc() + // startSATokenControllerInit is the original constructor. + saTokenControllerInit := saTokenControllerDescriptor.GetControllerConstructor() - // Wrap saTokenControllerDescriptor to signal readiness for migration after starting - // the controller. - saTokenControllerDescriptor.initFunc = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - defer close(leaderMigrator.MigrationReady) - return startSATokenControllerInit(ctx, controllerContext, controllerName) + // Wrap saTokenControllerDescriptor to signal readiness for migration after starting the controller. + saTokenControllerDescriptor.constructor = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + ctrl, err := saTokenControllerInit(ctx, controllerContext, controllerName) + if err != nil { + return nil, err + } + + // This wrapping is not exactly flawless as RunControllers uses type casting, + // which is now not possible for the wrapped controller. + // This fortunately doesn't matter for this particular controller. + return newControllerLoop(func(ctx context.Context) { + close(leaderMigrator.MigrationReady) + ctrl.Run(ctx) + }, controllerName), nil } } @@ -467,187 +492,22 @@ func (c ControllerContext) IsControllerEnabled(controllerDescriptor *ControllerD return genericcontrollermanager.IsControllerEnabled(controllerDescriptor.Name(), controllersDisabledByDefault, c.ComponentConfig.Generic.Controllers) } -// InitFunc is used to launch a particular controller. It returns a controller -// that can optionally implement other interfaces so that the controller manager -// can support the requested features. -// The returned controller may be nil, which will be considered an anonymous controller -// that requests no additional features from the controller manager. -// Any error returned will cause the controller process to `Fatal` -// The bool indicates whether the controller was enabled. -type InitFunc func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller controller.Interface, enabled bool, err error) - -type ControllerDescriptor struct { - name string - initFunc InitFunc - requiredFeatureGates []featuregate.Feature - aliases []string - isDisabledByDefault bool - isCloudProviderController bool - requiresSpecialHandling bool -} - -func (r *ControllerDescriptor) Name() string { - return r.name -} - -func (r *ControllerDescriptor) GetInitFunc() InitFunc { - return r.initFunc -} - -func (r *ControllerDescriptor) GetRequiredFeatureGates() []featuregate.Feature { - return append([]featuregate.Feature(nil), r.requiredFeatureGates...) -} - -// GetAliases returns aliases to ensure backwards compatibility and should never be removed! -// Only addition of new aliases is allowed, and only when a canonical name is changed (please see CHANGE POLICY of controller names) -func (r *ControllerDescriptor) GetAliases() []string { - return append([]string(nil), r.aliases...) -} - -func (r *ControllerDescriptor) IsDisabledByDefault() bool { - return r.isDisabledByDefault -} - -func (r *ControllerDescriptor) IsCloudProviderController() bool { - return r.isCloudProviderController -} - -// RequiresSpecialHandling should return true only in a special non-generic controllers like ServiceAccountTokenController -func (r *ControllerDescriptor) RequiresSpecialHandling() bool { - return r.requiresSpecialHandling -} - -// KnownControllers returns all known controllers's name -func KnownControllers() []string { - return sets.StringKeySet(NewControllerDescriptors()).List() -} - -// ControllerAliases returns a mapping of aliases to canonical controller names -func ControllerAliases() map[string]string { - aliases := map[string]string{} - for name, c := range NewControllerDescriptors() { - for _, alias := range c.GetAliases() { - aliases[alias] = name - } - } - return aliases -} - -func ControllersDisabledByDefault() []string { - var controllersDisabledByDefault []string - - for name, c := range NewControllerDescriptors() { - if c.IsDisabledByDefault() { - controllersDisabledByDefault = append(controllersDisabledByDefault, name) - } +// NewClientConfig is a shortcut for ClientBuilder.Config. It wraps the error with an additional message. +func (c ControllerContext) NewClientConfig(name string) (*restclient.Config, error) { + config, err := c.ClientBuilder.Config(name) + if err != nil { + return nil, fmt.Errorf("failed to create Kubernetes client config for %q: %w", name, err) } - sort.Strings(controllersDisabledByDefault) - return controllersDisabledByDefault + return config, nil } -// NewControllerDescriptors is a public map of named controller groups (you can start more than one in an init func) -// paired to their ControllerDescriptor wrapper object that includes InitFunc. -// This allows for structured downstream composition and subdivision. -func NewControllerDescriptors() map[string]*ControllerDescriptor { - controllers := map[string]*ControllerDescriptor{} - aliases := sets.NewString() - - // All the controllers must fulfil common constraints, or else we will explode. - register := func(controllerDesc *ControllerDescriptor) { - if controllerDesc == nil { - panic("received nil controller for a registration") - } - name := controllerDesc.Name() - if len(name) == 0 { - panic("received controller without a name for a registration") - } - if _, found := controllers[name]; found { - panic(fmt.Sprintf("controller name %q was registered twice", name)) - } - if controllerDesc.GetInitFunc() == nil { - panic(fmt.Sprintf("controller %q does not have an init function", name)) - } - - for _, alias := range controllerDesc.GetAliases() { - if aliases.Has(alias) { - panic(fmt.Sprintf("controller %q has a duplicate alias %q", name, alias)) - } - aliases.Insert(alias) - } - - controllers[name] = controllerDesc - } - - // First add "special" controllers that aren't initialized normally. These controllers cannot be initialized - // in the main controller loop initialization, so we add them here only for the metadata and duplication detection. - // app.ControllerDescriptor#RequiresSpecialHandling should return true for such controllers - // The only known special case is the ServiceAccountTokenController which *must* be started - // first to ensure that the SA tokens for future controllers will exist. Think very carefully before adding new - // special controllers. - register(newServiceAccountTokenControllerDescriptor(nil)) - - register(newEndpointsControllerDescriptor()) - register(newEndpointSliceControllerDescriptor()) - register(newEndpointSliceMirroringControllerDescriptor()) - register(newReplicationControllerDescriptor()) - register(newPodGarbageCollectorControllerDescriptor()) - register(newResourceQuotaControllerDescriptor()) - register(newNamespaceControllerDescriptor()) - register(newServiceAccountControllerDescriptor()) - register(newGarbageCollectorControllerDescriptor()) - register(newDaemonSetControllerDescriptor()) - register(newJobControllerDescriptor()) - register(newDeploymentControllerDescriptor()) - register(newReplicaSetControllerDescriptor()) - register(newHorizontalPodAutoscalerControllerDescriptor()) - register(newDisruptionControllerDescriptor()) - register(newStatefulSetControllerDescriptor()) - register(newCronJobControllerDescriptor()) - register(newCertificateSigningRequestSigningControllerDescriptor()) - register(newCertificateSigningRequestApprovingControllerDescriptor()) - register(newCertificateSigningRequestCleanerControllerDescriptor()) - register(newPodCertificateRequestCleanerControllerDescriptor()) - register(newTTLControllerDescriptor()) - register(newBootstrapSignerControllerDescriptor()) - register(newTokenCleanerControllerDescriptor()) - register(newNodeIpamControllerDescriptor()) - register(newNodeLifecycleControllerDescriptor()) - - register(newServiceLBControllerDescriptor()) // cloud provider controller - register(newNodeRouteControllerDescriptor()) // cloud provider controller - register(newCloudNodeLifecycleControllerDescriptor()) // cloud provider controller - - register(newPersistentVolumeBinderControllerDescriptor()) - register(newPersistentVolumeAttachDetachControllerDescriptor()) - register(newPersistentVolumeExpanderControllerDescriptor()) - register(newClusterRoleAggregrationControllerDescriptor()) - register(newPersistentVolumeClaimProtectionControllerDescriptor()) - register(newPersistentVolumeProtectionControllerDescriptor()) - register(newVolumeAttributesClassProtectionControllerDescriptor()) - register(newTTLAfterFinishedControllerDescriptor()) - register(newRootCACertificatePublisherControllerDescriptor()) - register(newKubeAPIServerSignerClusterTrustBundledPublisherDescriptor()) - register(newServiceCACertPublisher()) - register(newEphemeralVolumeControllerDescriptor()) - - // feature gated - register(newStorageVersionGarbageCollectorControllerDescriptor()) - register(newResourceClaimControllerDescriptor()) - register(newDeviceTaintEvictionControllerDescriptor()) - register(newLegacyServiceAccountTokenCleanerControllerDescriptor()) - register(newValidatingAdmissionPolicyStatusControllerDescriptor()) - register(newTaintEvictionControllerDescriptor()) - register(newServiceCIDRsControllerDescriptor()) - register(newStorageVersionMigratorControllerDescriptor()) - register(newSELinuxWarningControllerDescriptor()) - - for _, alias := range aliases.UnsortedList() { - if _, ok := controllers[alias]; ok { - panic(fmt.Sprintf("alias %q conflicts with a controller name", alias)) - } +// NewClient is a shortcut for ClientBuilder.Client. It wraps the error with an additional message. +func (c ControllerContext) NewClient(name string) (kubernetes.Interface, error) { + client, err := c.ClientBuilder.Client(name) + if err != nil { + return nil, fmt.Errorf("failed to create Kubernetes client for %q: %w", name, err) } - - return controllers + return client, nil } // CreateControllerContext creates a context struct containing references to resources needed by the @@ -664,7 +524,11 @@ func CreateControllerContext(ctx context.Context, s *config.CompletedConfig, roo return obj, nil } - versionedClient := rootClientBuilder.ClientOrDie("shared-informers") + versionedClient, err := rootClientBuilder.Client("shared-informers") + if err != nil { + return ControllerContext{}, fmt.Errorf("failed to create Kubernetes client for %q: %w", "shared-informers", err) + } + var sharedInformers informers.SharedInformerFactory if InformerFactoryOverride == nil { sharedInformers = informers.NewSharedInformerFactoryWithOptions(versionedClient, ResyncPeriod(s)(), informers.WithTransform(trim)) @@ -672,17 +536,30 @@ func CreateControllerContext(ctx context.Context, s *config.CompletedConfig, roo sharedInformers = InformerFactoryOverride } - metadataClient := metadata.NewForConfigOrDie(rootClientBuilder.ConfigOrDie("metadata-informers")) + metadataConfig, err := rootClientBuilder.Config("metadata-informers") + if err != nil { + return ControllerContext{}, fmt.Errorf("failed to create metadata client config: %w", err) + } + + metadataClient, err := metadata.NewForConfig(metadataConfig) + if err != nil { + return ControllerContext{}, fmt.Errorf("failed to create metadata client: %w", err) + } + metadataInformers := metadatainformer.NewSharedInformerFactoryWithOptions(metadataClient, ResyncPeriod(s)(), metadatainformer.WithTransform(trim)) // If apiserver is not running we should wait for some time and fail only then. This is particularly // important when we start apiserver and controller manager at the same time. if err := genericcontrollermanager.WaitForAPIServer(versionedClient, 10*time.Second); err != nil { - return ControllerContext{}, fmt.Errorf("failed to wait for apiserver being healthy: %v", err) + return ControllerContext{}, fmt.Errorf("failed to wait for apiserver being healthy: %w", err) } // Use a discovery client capable of being refreshed. - discoveryClient := rootClientBuilder.DiscoveryClientOrDie("controller-discovery") + discoveryClient, err := rootClientBuilder.DiscoveryClient("controller-discovery") + if err != nil { + return ControllerContext{}, fmt.Errorf("failed to create discovery client: %w", err) + } + cachedClient := cacheddiscovery.NewMemCacheClient(discoveryClient) restMapper := restmapper.NewDeferredDiscoveryRESTMapper(cachedClient) go wait.Until(func() { @@ -722,27 +599,68 @@ func CreateControllerContext(ctx context.Context, s *config.CompletedConfig, roo return controllerContext, nil } -// StartControllers starts a set of controllers with a specified ControllerContext -func StartControllers(ctx context.Context, controllerCtx ControllerContext, controllerDescriptors map[string]*ControllerDescriptor, - unsecuredMux *mux.PathRecorderMux, healthzHandler *controllerhealthz.MutableHealthzHandler) error { - var controllerChecks []healthz.HealthChecker +// HealthCheckAdder is an interface to represent a healthz handler. +// The extra level of indirection is useful for testing. +type HealthCheckAdder interface { + AddHealthChecker(checks ...healthz.HealthChecker) +} - // Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest - // If this fails, just return here and fail since other controllers won't be able to get credentials. - if serviceAccountTokenControllerDescriptor, ok := controllerDescriptors[names.ServiceAccountTokenController]; ok { - check, err := StartController(ctx, controllerCtx, serviceAccountTokenControllerDescriptor, unsecuredMux) +// BuildControllers builds all controllers in the given descriptor map. Disabled controllers are obviously skipped. +// +// A health check is registered for each controller using the controller name. The default check always passes. +// If the controller implements controller.HealthCheckable, though, the given check is used. +// The controller can also implement controller.Debuggable, in which case the debug handler is registered with the given mux. +func BuildControllers(ctx context.Context, controllerCtx ControllerContext, controllerDescriptors map[string]*ControllerDescriptor, + unsecuredMux *mux.PathRecorderMux, healthzHandler HealthCheckAdder) ([]Controller, error) { + logger := klog.FromContext(ctx) + var ( + controllers []Controller + checks []healthz.HealthChecker + ) + buildController := func(controllerDesc *ControllerDescriptor) error { + controllerName := controllerDesc.Name() + ctrl, err := controllerDesc.BuildController(ctx, controllerCtx) if err != nil { + logger.Error(err, "Error initializing a controller", "controller", controllerName) return err } - if check != nil { - // HealthChecker should be present when controller has started - controllerChecks = append(controllerChecks, check) + if ctrl == nil { + logger.Info("Warning: skipping controller", "controller", controllerName) + return nil + } + + check := controllerhealthz.NamedPingChecker(controllerName) + // check if the controller supports and requests a debugHandler, + // and it needs the unsecuredMux to mount the handler onto. + if debuggable, ok := ctrl.(controller.Debuggable); ok && unsecuredMux != nil { + if debugHandler := debuggable.DebuggingHandler(); debugHandler != nil { + basePath := "/debug/controllers/" + controllerName + unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler)) + unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler)) + } + } + if healthCheckable, ok := ctrl.(controller.HealthCheckable); ok { + if realCheck := healthCheckable.HealthChecker(); realCheck != nil { + check = controllerhealthz.NamedHealthChecker(controllerName, realCheck) + } + } + + controllers = append(controllers, ctrl) + checks = append(checks, check) + return nil + } + + // Always start the SA token controller first using a full-power client, since it needs to mint tokens for the rest + // If this fails, just return here and fail since other controllers won't be able to get credentials. + if serviceAccountTokenControllerDescriptor, ok := controllerDescriptors[names.ServiceAccountTokenController]; ok { + if err := buildController(serviceAccountTokenControllerDescriptor); err != nil { + return nil, err } } // Each controller is passed a context where the logger has the name of // the controller set through WithName. That name then becomes the prefix of - // of all log messages emitted by that controller. + // all log messages emitted by that controller. // // In StartController, an explicit "controller" key is used instead, for two reasons: // - while contextual logging is alpha, klog.LoggerWithName is still a no-op, @@ -754,139 +672,124 @@ func StartControllers(ctx context.Context, controllerCtx ControllerContext, cont continue } - check, err := StartController(ctx, controllerCtx, controllerDesc, unsecuredMux) - if err != nil { - return err - } - if check != nil { - // HealthChecker should be present when controller has started - controllerChecks = append(controllerChecks, check) + if !controllerCtx.IsControllerEnabled(controllerDesc) { + logger.Info("Warning: controller is disabled", "controller", controllerDesc.Name()) + continue } - } - - healthzHandler.AddHealthChecker(controllerChecks...) - - return nil -} - -// StartController starts a controller with a specified ControllerContext -// and performs required pre- and post- checks/actions -func StartController(ctx context.Context, controllerCtx ControllerContext, controllerDescriptor *ControllerDescriptor, - unsecuredMux *mux.PathRecorderMux) (healthz.HealthChecker, error) { - logger := klog.FromContext(ctx) - controllerName := controllerDescriptor.Name() - for _, featureGate := range controllerDescriptor.GetRequiredFeatureGates() { - if !utilfeature.DefaultFeatureGate.Enabled(featureGate) { - logger.Info("Controller is disabled by a feature gate", "controller", controllerName, "requiredFeatureGates", controllerDescriptor.GetRequiredFeatureGates()) - return nil, nil + if err := buildController(controllerDesc); err != nil { + return nil, err } } - if controllerDescriptor.IsCloudProviderController() { - logger.Info("Skipping a cloud provider controller", "controller", controllerName) - return nil, nil - } - - if !controllerCtx.IsControllerEnabled(controllerDescriptor) { - logger.Info("Warning: controller is disabled", "controller", controllerName) - return nil, nil + // Register the checks. + if len(checks) > 0 { + healthzHandler.AddHealthChecker(checks...) } + return controllers, nil +} - time.Sleep(wait.Jitter(controllerCtx.ComponentConfig.Generic.ControllerStartInterval.Duration, ControllerStartJitter)) +// RunControllers runs all controllers concurrently and blocks until the context is cancelled and all controllers are terminated. +// +// Once the context is cancelled, RunControllers waits for shutdownTimeout for all controllers to terminate. +// When the timeout is reached, the function unblocks and returns false. +// Zero shutdown timeout means that there is no timeout. +func RunControllers(ctx context.Context, controllerCtx ControllerContext, controllers []Controller, + controllerStartJitterMaxFactor float64, shutdownTimeout time.Duration) bool { + logger := klog.FromContext(ctx) - logger.V(1).Info("Starting controller", "controller", controllerName) + // We gather running controllers names for logging purposes. + // When the context is cancelled, the controllers still running are logged periodically. + runningControllers := sets.New[string]() + var runningControllersLock sync.Mutex - initFunc := controllerDescriptor.GetInitFunc() - ctrl, started, err := initFunc(klog.NewContext(ctx, klog.LoggerWithName(logger, controllerName)), controllerCtx, controllerName) - if err != nil { - logger.Error(err, "Error starting controller", "controller", controllerName) - return nil, err - } - if !started { - logger.Info("Warning: skipping controller", "controller", controllerName) - return nil, nil - } + loggingCtx, cancelLoggingCtx := context.WithCancel(context.Background()) + defer cancelLoggingCtx() + go func() { + // Only start logging when terminating. + select { + case <-ctx.Done(): + case <-loggingCtx.Done(): + return + } - check := controllerhealthz.NamedPingChecker(controllerName) - if ctrl != nil { - // check if the controller supports and requests a debugHandler - // and it needs the unsecuredMux to mount the handler onto. - if debuggable, ok := ctrl.(controller.Debuggable); ok && unsecuredMux != nil { - if debugHandler := debuggable.DebuggingHandler(); debugHandler != nil { - basePath := "/debug/controllers/" + controllerName - unsecuredMux.UnlistedHandle(basePath, http.StripPrefix(basePath, debugHandler)) - unsecuredMux.UnlistedHandlePrefix(basePath+"/", http.StripPrefix(basePath, debugHandler)) - } + // Regularly print the controllers that still haven't returned. + logPeriod := shutdownTimeout / 3 + if logPeriod == 0 { + logPeriod = 5 * time.Second } - if healthCheckable, ok := ctrl.(controller.HealthCheckable); ok { - if realCheck := healthCheckable.HealthChecker(); realCheck != nil { - check = controllerhealthz.NamedHealthChecker(controllerName, realCheck) + ticker := time.NewTicker(logPeriod) + defer ticker.Stop() + for { + select { + case <-ticker.C: + runningControllersLock.Lock() + running := sets.List(runningControllers) + runningControllersLock.Unlock() + + logger.Info("Still waiting for some controllers to terminate...", "runningControllers", running) + + case <-loggingCtx.Done(): + return } } - } - - logger.Info("Started controller", "controller", controllerName) - return check, nil -} + }() -// serviceAccountTokenControllerStarter is special because it must run first to set up permissions for other controllers. -// It cannot use the "normal" client builder, so it tracks its own. -func newServiceAccountTokenControllerDescriptor(rootClientBuilder clientbuilder.ControllerClientBuilder) *ControllerDescriptor { - return &ControllerDescriptor{ - name: names.ServiceAccountTokenController, - aliases: []string{"serviceaccount-token"}, - initFunc: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - return startServiceAccountTokenController(ctx, controllerContext, controllerName, rootClientBuilder) - }, - // will make sure it runs first before other controllers - requiresSpecialHandling: true, - } -} + terminatedCh := make(chan struct{}) + go func() { + defer close(terminatedCh) + var wg sync.WaitGroup + wg.Add(len(controllers)) + for _, controller := range controllers { + go func() { + defer wg.Done() + + // It would be better to unblock and return on context cancelled here, + // but that makes tests more flaky regarding timing. + time.Sleep(wait.Jitter(controllerCtx.ComponentConfig.Generic.ControllerStartInterval.Duration, controllerStartJitterMaxFactor)) + + logger.V(1).Info("Controller starting...", "controller", controller.Name()) + + runningControllersLock.Lock() + runningControllers.Insert(controller.Name()) + runningControllersLock.Unlock() + + defer func() { + logger.V(1).Info("Controller terminated", "controller", controller.Name()) + + runningControllersLock.Lock() + runningControllers.Delete(controller.Name()) + runningControllersLock.Unlock() + }() + controller.Run(ctx) + }() + } + wg.Wait() + logger.Info("All controllers terminated") + }() -func startServiceAccountTokenController(ctx context.Context, controllerContext ControllerContext, controllerName string, rootClientBuilder clientbuilder.ControllerClientBuilder) (controller.Interface, bool, error) { - logger := klog.FromContext(ctx) - if len(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile) == 0 { - logger.Info("Controller is disabled because there is no private key", "controller", controllerName) - return nil, false, nil - } - privateKey, err := keyutil.PrivateKeyFromFile(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile) - if err != nil { - return nil, true, fmt.Errorf("error reading key for service account token controller: %v", err) + // Wait for a signal to terminate. + select { + case <-ctx.Done(): + case <-terminatedCh: + return true } - var rootCA []byte - if controllerContext.ComponentConfig.SAController.RootCAFile != "" { - if rootCA, err = readCA(controllerContext.ComponentConfig.SAController.RootCAFile); err != nil { - return nil, true, fmt.Errorf("error parsing root-ca-file at %s: %v", controllerContext.ComponentConfig.SAController.RootCAFile, err) - } - } else { - rootCA = rootClientBuilder.ConfigOrDie("tokens-controller").CAData + // Wait for the shutdown timeout. + var shutdownCh <-chan time.Time + if shutdownTimeout > 0 { + shutdownCh = time.After(shutdownTimeout) } - - tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, privateKey) - if err != nil { - return nil, false, fmt.Errorf("failed to build token generator: %v", err) - } - tokenController, err := serviceaccountcontroller.NewTokensController( - logger, - controllerContext.InformerFactory.Core().V1().ServiceAccounts(), - controllerContext.InformerFactory.Core().V1().Secrets(), - rootClientBuilder.ClientOrDie("tokens-controller"), - applyOpenShiftServiceServingCertCA(serviceaccountcontroller.TokensControllerOptions{ - TokenGenerator: tokenGenerator, - RootCA: rootCA, - }), - ) - if err != nil { - return nil, true, fmt.Errorf("error creating Tokens controller: %v", err) + select { + case <-terminatedCh: + return true + case <-shutdownCh: + runningControllersLock.Lock() + running := sets.List(runningControllers) + runningControllersLock.Unlock() + logger.Info("Controller shutdown timeout reached", "timeout", shutdownTimeout, "runningControllers", running) + return false } - go tokenController.Run(ctx, int(controllerContext.ComponentConfig.SAController.ConcurrentSATokenSyncs)) - - // start the first set of informers now so that other controllers can start - controllerContext.InformerFactory.Start(ctx.Done()) - - return nil, true, nil } func readCA(file string) ([]byte, error) { diff --git a/cmd/kube-controller-manager/app/controllermanager_test.go b/cmd/kube-controller-manager/app/controllermanager_test.go index 7753deefefa0f..bfb1b34f33180 100644 --- a/cmd/kube-controller-manager/app/controllermanager_test.go +++ b/cmd/kube-controller-manager/app/controllermanager_test.go @@ -21,16 +21,17 @@ import ( "regexp" "strings" "testing" + "time" "github.com/google/go-cmp/cmp" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/version" + "k8s.io/apiserver/pkg/server/healthz" utilfeature "k8s.io/apiserver/pkg/util/feature" cpnames "k8s.io/cloud-provider/names" "k8s.io/component-base/featuregate" featuregatetesting "k8s.io/component-base/featuregate/testing" - controllermanagercontroller "k8s.io/controller-manager/controller" "k8s.io/klog/v2/ktesting" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/features" @@ -116,13 +117,18 @@ func TestNewControllerDescriptorsShouldNotPanic(t *testing.T) { } func TestNewControllerDescriptorsAlwaysReturnsDescriptorsForAllControllers(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", false) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", false) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + "AllAlpha": false, + "AllBeta": false, + }) controllersWithoutFeatureGates := KnownControllers() - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", true) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", true) + // AllBeta must be enabled before AllAlpha to resolve dependencies. + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + "AllBeta": true, + "AllAlpha": true, + }) controllersWithFeatureGates := KnownControllers() @@ -207,19 +213,21 @@ func TestTaintEvictionControllerGating(t *testing.T) { initFuncCalled := false taintEvictionControllerDescriptor := NewControllerDescriptors()[names.TaintEvictionController] - taintEvictionControllerDescriptor.initFunc = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller controllermanagercontroller.Interface, enabled bool, err error) { + taintEvictionControllerDescriptor.constructor = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { initFuncCalled = true - return nil, true, nil + return newControllerLoop(func(ctx context.Context) {}, controllerName), nil } - healthCheck, err := StartController(ctx, controllerCtx, taintEvictionControllerDescriptor, nil) - if err != nil { + var healthChecks mockHealthCheckAdder + if err := runControllers(ctx, controllerCtx, map[string]*ControllerDescriptor{ + names.TaintEvictionController: taintEvictionControllerDescriptor, + }, &healthChecks); err != nil { t.Errorf("starting a TaintEvictionController controller should not return an error") } if test.expectInitFuncCall != initFuncCalled { t.Errorf("TaintEvictionController init call check failed: expected=%v, got=%v", test.expectInitFuncCall, initFuncCalled) } - hasHealthCheck := healthCheck != nil + hasHealthCheck := len(healthChecks.Checks) > 0 expectHealthCheck := test.expectInitFuncCall if expectHealthCheck != hasHealthCheck { t.Errorf("TaintEvictionController healthCheck check failed: expected=%v, got=%v", expectHealthCheck, hasHealthCheck) @@ -230,23 +238,96 @@ func TestTaintEvictionControllerGating(t *testing.T) { func TestNoCloudProviderControllerStarted(t *testing.T) { _, ctx := ktesting.NewTestContext(t) - ctx, cancel := context.WithCancel(ctx) - defer cancel() - controllerCtx := ControllerContext{} controllerCtx.ComponentConfig.Generic.Controllers = []string{"*"} - for _, controller := range NewControllerDescriptors() { + cpControllerDescriptors := make(map[string]*ControllerDescriptor) + for controllerName, controller := range NewControllerDescriptors() { if !controller.IsCloudProviderController() { continue } - controllerName := controller.Name() - checker, err := StartController(ctx, controllerCtx, controller, nil) - if err != nil { - t.Errorf("Error starting controller %q: %v", controllerName, err) - } - if checker != nil { - t.Errorf("Controller %q should not be started", controllerName) + controller.constructor = func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + return newControllerLoop(func(ctx context.Context) { + t.Error("Controller should not be started:", controllerName) + }, controllerName), nil } + + cpControllerDescriptors[controllerName] = controller + } + + var healthChecks mockHealthCheckAdder + if err := runControllers(ctx, controllerCtx, cpControllerDescriptors, &healthChecks); err != nil { + t.Error("Failed to start controllers:", err) + } +} + +func TestRunControllers(t *testing.T) { + testCases := []struct { + name string + newController func(ctx context.Context) Controller + shutdownTimeout time.Duration + expectedCleanTermination bool + }{ + { + name: "clean shutdown", + newController: func(testCtx context.Context) Controller { + return newControllerLoop(func(ctx context.Context) { + <-ctx.Done() + }, "controller-A") + }, + shutdownTimeout: 10 * time.Second, + expectedCleanTermination: true, + }, + { + name: "shutdown timeout", + newController: func(testCtx context.Context) Controller { + return newControllerLoop(func(ctx context.Context) { + <-testCtx.Done() + }, "controller-A") + }, + shutdownTimeout: 50 * time.Millisecond, + expectedCleanTermination: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, ctx := ktesting.NewTestContext(t) + controllerCtx := ControllerContext{} + + // testCtx is used to make sure the controller failing to shut down can exit after the test is finished. + testCtx, cancelTest := context.WithCancel(ctx) + defer cancelTest() + + // ctx is used to wait in the controller for shutdown and also to start the shutdown timeout in RunControllers. + // To start the shutdown timeout immediately, we start with a cancelled context already. + ctx, cancelController := context.WithCancel(ctx) + cancelController() + + cleanShutdown := RunControllers(ctx, controllerCtx, []Controller{tc.newController(testCtx)}, 0, tc.shutdownTimeout) + if cleanShutdown != tc.expectedCleanTermination { + t.Errorf("expected clean shutdown %v, got %v", tc.expectedCleanTermination, cleanShutdown) + } + }) + } +} + +type mockHealthCheckAdder struct { + Checks []healthz.HealthChecker +} + +func (m *mockHealthCheckAdder) AddHealthChecker(checks ...healthz.HealthChecker) { + m.Checks = append(m.Checks, checks...) +} + +func runControllers( + ctx context.Context, controllerCtx ControllerContext, + controllerDescriptors map[string]*ControllerDescriptor, healthzChecks HealthCheckAdder, +) error { + controllers, err := BuildControllers(ctx, controllerCtx, controllerDescriptors, nil, healthzChecks) + if err != nil { + return err } + RunControllers(ctx, controllerCtx, controllers, 0, 0) + return nil } diff --git a/cmd/kube-controller-manager/app/core.go b/cmd/kube-controller-manager/app/core.go index 8cf5465578a9e..309286be8bbee 100644 --- a/cmd/kube-controller-manager/app/core.go +++ b/cmd/kube-controller-manager/app/core.go @@ -31,6 +31,7 @@ import ( genericfeatures "k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/quota/v1/generic" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/discovery" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/metadata" restclient "k8s.io/client-go/rest" @@ -82,47 +83,43 @@ const ( func newServiceLBControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: cpnames.ServiceLBController, - aliases: []string{"service"}, - initFunc: startServiceLBController, + name: cpnames.ServiceLBController, + aliases: []string{"service"}, + constructor: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + logger := klog.FromContext(ctx) + logger.Info("Warning: service-controller is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure service controller.") + return nil, nil + }, isCloudProviderController: true, } } -func startServiceLBController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - logger := klog.FromContext(ctx) - logger.Info("Warning: service-controller is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure service controller.") - return nil, false, nil -} func newNodeIpamControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.NodeIpamController, - aliases: []string{"nodeipam"}, - initFunc: startNodeIpamController, + name: names.NodeIpamController, + aliases: []string{"nodeipam"}, + constructor: newNodeIpamController, } } -func startNodeIpamController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - var serviceCIDR *net.IPNet - var secondaryServiceCIDR *net.IPNet - logger := klog.FromContext(ctx) - - // should we start nodeIPAM +func newNodeIpamController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { if !controllerContext.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs { - return nil, false, nil + return nil, nil } - if controllerContext.ComponentConfig.KubeCloudShared.CIDRAllocatorType == string(ipam.CloudAllocatorType) { // Cannot run cloud ipam controller if cloud provider is nil (--cloud-provider not set or set to 'external') - return nil, false, errors.New("--cidr-allocator-type is set to 'CloudAllocator' but cloud provider is not configured") + return nil, errors.New("--cidr-allocator-type is set to 'CloudAllocator' but cloud provider is not configured") } clusterCIDRs, err := validateCIDRs(controllerContext.ComponentConfig.KubeCloudShared.ClusterCIDR) if err != nil { - return nil, false, err + return nil, err } // service cidr processing + var serviceCIDR *net.IPNet + var secondaryServiceCIDR *net.IPNet + logger := klog.FromContext(ctx) if len(strings.TrimSpace(controllerContext.ComponentConfig.NodeIPAMController.ServiceCIDR)) != 0 { _, serviceCIDR, err = netutils.ParseCIDRSloppy(controllerContext.ComponentConfig.NodeIPAMController.ServiceCIDR) if err != nil { @@ -142,10 +139,10 @@ func startNodeIpamController(ctx context.Context, controllerContext ControllerCo // should be dual stack (from different IPFamilies) dualstackServiceCIDR, err := netutils.IsDualStackCIDRs([]*net.IPNet{serviceCIDR, secondaryServiceCIDR}) if err != nil { - return nil, false, fmt.Errorf("failed to perform dualstack check on serviceCIDR and secondaryServiceCIDR error: %w", err) + return nil, fmt.Errorf("failed to perform dualstack check on serviceCIDR and secondaryServiceCIDR error: %w", err) } if !dualstackServiceCIDR { - return nil, false, fmt.Errorf("serviceCIDR and secondaryServiceCIDR are not dualstack (from different IPfamiles)") + return nil, fmt.Errorf("serviceCIDR and secondaryServiceCIDR are not dualstack (from different IPfamiles)") } } @@ -153,14 +150,19 @@ func startNodeIpamController(ctx context.Context, controllerContext ControllerCo // --node-cidr-mask-size flag is incompatible with dual stack clusters. nodeCIDRMaskSizes, err := setNodeCIDRMaskSizes(controllerContext.ComponentConfig.NodeIPAMController, clusterCIDRs) if err != nil { - return nil, false, err + return nil, err + } + + client, err := controllerContext.NewClient("node-controller") + if err != nil { + return nil, err } nodeIpamController, err := nodeipamcontroller.NewNodeIpamController( ctx, controllerContext.InformerFactory.Core().V1().Nodes(), nil, // no cloud provider on kube-controller-manager since v1.31 (KEP-2395) - controllerContext.ClientBuilder.ClientOrDie("node-controller"), + client, clusterCIDRs, serviceCIDR, secondaryServiceCIDR, @@ -168,29 +170,36 @@ func startNodeIpamController(ctx context.Context, controllerContext ControllerCo ipam.CIDRAllocatorType(controllerContext.ComponentConfig.KubeCloudShared.CIDRAllocatorType), ) if err != nil { - return nil, true, err + return nil, err } - go nodeIpamController.RunWithMetrics(ctx, controllerContext.ControllerManagerMetrics) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + nodeIpamController.RunWithMetrics(ctx, controllerContext.ControllerManagerMetrics) + }, controllerName), nil } func newNodeLifecycleControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.NodeLifecycleController, - aliases: []string{"nodelifecycle"}, - initFunc: startNodeLifecycleController, + name: names.NodeLifecycleController, + aliases: []string{"nodelifecycle"}, + constructor: newNodeLifecycleController, } } -func startNodeLifecycleController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - lifecycleController, err := lifecyclecontroller.NewNodeLifecycleController( +func newNodeLifecycleController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("node-controller") + if err != nil { + return nil, err + } + + nlc, err := lifecyclecontroller.NewNodeLifecycleController( ctx, controllerContext.InformerFactory.Coordination().V1().Leases(), controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Nodes(), controllerContext.InformerFactory.Apps().V1().DaemonSets(), // node lifecycle controller uses existing cluster role from node-controller - controllerContext.ClientBuilder.ClientOrDie("node-controller"), + client, controllerContext.ComponentConfig.KubeCloudShared.NodeMonitorPeriod.Duration, controllerContext.ComponentConfig.NodeLifecycleController.NodeStartupGracePeriod.Duration, controllerContext.ComponentConfig.NodeLifecycleController.NodeMonitorGracePeriod.Duration, @@ -200,42 +209,49 @@ func startNodeLifecycleController(ctx context.Context, controllerContext Control controllerContext.ComponentConfig.NodeLifecycleController.UnhealthyZoneThreshold, ) if err != nil { - return nil, true, err + return nil, err } - go lifecycleController.Run(ctx) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + nlc.Run(ctx) + }, controllerName), nil } func newTaintEvictionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.TaintEvictionController, - initFunc: startTaintEvictionController, + name: names.TaintEvictionController, + constructor: newTaintEvictionController, requiredFeatureGates: []featuregate.Feature{ features.SeparateTaintEvictionController, }, } } -func startTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - taintEvictionController, err := tainteviction.New( +func newTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + // taint-manager uses existing cluster role from node-controller + client, err := controllerContext.NewClient("node-controller") + if err != nil { + return nil, err + } + + tec, err := tainteviction.New( ctx, - // taint-manager uses existing cluster role from node-controller - controllerContext.ClientBuilder.ClientOrDie("node-controller"), + client, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Nodes(), controllerName, ) if err != nil { - return nil, false, err + return nil, err } - go taintEvictionController.Run(ctx) - return nil, true, nil + + return newControllerLoop(tec.Run, controllerName), nil } func newDeviceTaintEvictionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.DeviceTaintEvictionController, - initFunc: startDeviceTaintEvictionController, + name: names.DeviceTaintEvictionController, + constructor: newDeviceTaintEvictionController, requiredFeatureGates: []featuregate.Feature{ // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing these feature gates. features.DynamicResourceAllocation, @@ -244,9 +260,14 @@ func newDeviceTaintEvictionControllerDescriptor() *ControllerDescriptor { } } -func startDeviceTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newDeviceTaintEvictionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient(names.DeviceTaintEvictionController) + if err != nil { + return nil, err + } + deviceTaintEvictionController := devicetainteviction.New( - controllerContext.ClientBuilder.ClientOrDie(names.DeviceTaintEvictionController), + client, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Resource().V1().ResourceClaims(), controllerContext.InformerFactory.Resource().V1().ResourceSlices(), @@ -254,61 +275,62 @@ func startDeviceTaintEvictionController(ctx context.Context, controllerContext C controllerContext.InformerFactory.Resource().V1().DeviceClasses(), controllerName, ) - go func() { - if err := deviceTaintEvictionController.Run(ctx); err != nil { + return newControllerLoop(func(ctx context.Context) { + if err := deviceTaintEvictionController.Run(ctx, int(controllerContext.ComponentConfig.DeviceTaintEvictionController.ConcurrentSyncs)); err != nil { klog.FromContext(ctx).Error(err, "Device taint processing leading to Pod eviction failed and is now paused") } - }() - return nil, true, nil + <-ctx.Done() + }, controllerName), nil } func newCloudNodeLifecycleControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: cpnames.CloudNodeLifecycleController, - aliases: []string{"cloud-node-lifecycle"}, - initFunc: startCloudNodeLifecycleController, + name: cpnames.CloudNodeLifecycleController, + aliases: []string{"cloud-node-lifecycle"}, + constructor: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + logger := klog.FromContext(ctx) + logger.Info("Warning: node-controller is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure node lifecyle controller.") + return nil, nil + }, isCloudProviderController: true, } } -func startCloudNodeLifecycleController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - logger := klog.FromContext(ctx) - logger.Info("Warning: node-controller is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure node lifecyle controller.") - return nil, false, nil -} - func newNodeRouteControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: cpnames.NodeRouteController, - aliases: []string{"route"}, - initFunc: startNodeRouteController, + name: cpnames.NodeRouteController, + aliases: []string{"route"}, + constructor: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + logger := klog.FromContext(ctx) + logger.Info("Warning: configure-cloud-routes is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure cloud provider routes.") + return nil, nil + }, isCloudProviderController: true, } } -func startNodeRouteController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - logger := klog.FromContext(ctx) - logger.Info("Warning: configure-cloud-routes is set, but no cloud provider functionality is available in kube-controller-manger (KEP-2395). Will not configure cloud provider routes.") - return nil, false, nil -} - func newPersistentVolumeBinderControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PersistentVolumeBinderController, - aliases: []string{"persistentvolume-binder"}, - initFunc: startPersistentVolumeBinderController, + name: names.PersistentVolumeBinderController, + aliases: []string{"persistentvolume-binder"}, + constructor: newPersistentVolumeBinderController, } } -func startPersistentVolumeBinderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newPersistentVolumeBinderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { logger := klog.FromContext(ctx) plugins, err := ProbeProvisionableRecyclableVolumePlugins(logger, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration) if err != nil { - return nil, true, fmt.Errorf("failed to probe volume plugins when starting persistentvolume controller: %v", err) + return nil, fmt.Errorf("failed to probe volume plugins when starting persistentvolume controller: %w", err) + } + + client, err := controllerContext.NewClient("persistent-volume-binder") + if err != nil { + return nil, err } params := persistentvolumecontroller.ControllerParameters{ - KubeClient: controllerContext.ClientBuilder.ClientOrDie("persistent-volume-binder"), + KubeClient: client, SyncPeriod: controllerContext.ComponentConfig.PersistentVolumeBinderController.PVClaimBinderSyncPeriod.Duration, VolumePlugins: plugins, VolumeInformer: controllerContext.InformerFactory.Core().V1().PersistentVolumes(), @@ -318,214 +340,264 @@ func startPersistentVolumeBinderController(ctx context.Context, controllerContex NodeInformer: controllerContext.InformerFactory.Core().V1().Nodes(), EnableDynamicProvisioning: controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration.EnableDynamicProvisioning, } - volumeController, volumeControllerErr := persistentvolumecontroller.NewController(ctx, params) - if volumeControllerErr != nil { - return nil, true, fmt.Errorf("failed to construct persistentvolume controller: %v", volumeControllerErr) + volumeController, err := persistentvolumecontroller.NewController(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to construct persistentvolume controller: %w", err) } - go volumeController.Run(ctx) - return nil, true, nil + + return newControllerLoop(volumeController.Run, controllerName), nil } func newPersistentVolumeAttachDetachControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PersistentVolumeAttachDetachController, - aliases: []string{"attachdetach"}, - initFunc: startPersistentVolumeAttachDetachController, + name: names.PersistentVolumeAttachDetachController, + aliases: []string{"attachdetach"}, + constructor: newPersistentVolumeAttachDetachController, } } -func startPersistentVolumeAttachDetachController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newPersistentVolumeAttachDetachController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { logger := klog.FromContext(ctx) csiNodeInformer := controllerContext.InformerFactory.Storage().V1().CSINodes() csiDriverInformer := controllerContext.InformerFactory.Storage().V1().CSIDrivers() plugins, err := ProbeAttachableVolumePlugins(logger, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration) if err != nil { - return nil, true, fmt.Errorf("failed to probe volume plugins when starting attach/detach controller: %v", err) + return nil, fmt.Errorf("failed to probe volume plugins when starting attach/detach controller: %w", err) + } + + client, err := controllerContext.NewClient("attachdetach-controller") + if err != nil { + return nil, err } ctx = klog.NewContext(ctx, logger) - attachDetachController, attachDetachControllerErr := - attachdetach.NewAttachDetachController( - ctx, - controllerContext.ClientBuilder.ClientOrDie("attachdetach-controller"), - controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.InformerFactory.Core().V1().Nodes(), - controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), - controllerContext.InformerFactory.Core().V1().PersistentVolumes(), - csiNodeInformer, - csiDriverInformer, - controllerContext.InformerFactory.Storage().V1().VolumeAttachments(), - plugins, - GetDynamicPluginProber(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration), - controllerContext.ComponentConfig.AttachDetachController.DisableAttachDetachReconcilerSync, - controllerContext.ComponentConfig.AttachDetachController.ReconcilerSyncLoopPeriod.Duration, - controllerContext.ComponentConfig.AttachDetachController.DisableForceDetachOnTimeout, - attachdetach.DefaultTimerConfig, - ) - if attachDetachControllerErr != nil { - return nil, true, fmt.Errorf("failed to start attach/detach controller: %v", attachDetachControllerErr) - } - go attachDetachController.Run(ctx) - return nil, true, nil + attachDetachController, err := attachdetach.NewAttachDetachController( + ctx, + client, + controllerContext.InformerFactory.Core().V1().Pods(), + controllerContext.InformerFactory.Core().V1().Nodes(), + controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), + controllerContext.InformerFactory.Core().V1().PersistentVolumes(), + csiNodeInformer, + csiDriverInformer, + controllerContext.InformerFactory.Storage().V1().VolumeAttachments(), + plugins, + GetDynamicPluginProber(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration), + controllerContext.ComponentConfig.AttachDetachController.DisableAttachDetachReconcilerSync, + controllerContext.ComponentConfig.AttachDetachController.ReconcilerSyncLoopPeriod.Duration, + controllerContext.ComponentConfig.AttachDetachController.DisableForceDetachOnTimeout, + attachdetach.DefaultTimerConfig, + ) + if err != nil { + return nil, fmt.Errorf("failed to start attach/detach controller: %w", err) + } + + return newControllerLoop(attachDetachController.Run, controllerName), nil } func newPersistentVolumeExpanderControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PersistentVolumeExpanderController, - aliases: []string{"persistentvolume-expander"}, - initFunc: startPersistentVolumeExpanderController, + name: names.PersistentVolumeExpanderController, + aliases: []string{"persistentvolume-expander"}, + constructor: newPersistentVolumeExpanderController, } } -func startPersistentVolumeExpanderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newPersistentVolumeExpanderController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { logger := klog.FromContext(ctx) plugins, err := ProbeExpandableVolumePlugins(logger, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration) if err != nil { - return nil, true, fmt.Errorf("failed to probe volume plugins when starting volume expand controller: %v", err) + return nil, fmt.Errorf("failed to probe volume plugins when starting volume expand controller: %w", err) } csiTranslator := csitrans.New() - expandController, expandControllerErr := expand.NewExpandController( + client, err := controllerContext.NewClient("expand-controller") + if err != nil { + return nil, err + } + + expandController, err := expand.NewExpandController( ctx, - controllerContext.ClientBuilder.ClientOrDie("expand-controller"), + client, controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), plugins, csiTranslator, csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate), ) - - if expandControllerErr != nil { - return nil, true, fmt.Errorf("failed to start volume expand controller: %v", expandControllerErr) + if err != nil { + return nil, fmt.Errorf("failed to init volume expand controller: %w", err) } - go expandController.Run(ctx) - return nil, true, nil + + return newControllerLoop(expandController.Run, controllerName), nil } func newEphemeralVolumeControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.EphemeralVolumeController, - aliases: []string{"ephemeral-volume"}, - initFunc: startEphemeralVolumeController, + name: names.EphemeralVolumeController, + aliases: []string{"ephemeral-volume"}, + constructor: newEphemeralVolumeController, } } -func startEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newEphemeralVolumeController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("ephemeral-volume-controller") + if err != nil { + return nil, err + } + ephemeralController, err := ephemeral.NewController( ctx, - controllerContext.ClientBuilder.ClientOrDie("ephemeral-volume-controller"), + client, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims()) if err != nil { - return nil, true, fmt.Errorf("failed to start ephemeral volume controller: %v", err) + return nil, fmt.Errorf("failed to init ephemeral volume controller: %w", err) } - go ephemeralController.Run(ctx, int(controllerContext.ComponentConfig.EphemeralVolumeController.ConcurrentEphemeralVolumeSyncs)) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + ephemeralController.Run(ctx, int(controllerContext.ComponentConfig.EphemeralVolumeController.ConcurrentEphemeralVolumeSyncs)) + }, controllerName), nil } const defaultResourceClaimControllerWorkers = 50 func newResourceClaimControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ResourceClaimController, - aliases: []string{"resource-claim-controller"}, - initFunc: startResourceClaimController, + name: names.ResourceClaimController, + aliases: []string{"resource-claim-controller"}, + constructor: newResourceClaimController, requiredFeatureGates: []featuregate.Feature{ features.DynamicResourceAllocation, // TODO update app.TestFeatureGatedControllersShouldNotDefineAliases when removing this feature }, } } -func startResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("resource-claim-controller") + if err != nil { + return nil, err + } + ephemeralController, err := resourceclaim.NewController( klog.FromContext(ctx), resourceclaim.Features{ AdminAccess: utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess), PrioritizedList: utilfeature.DefaultFeatureGate.Enabled(features.DRAPrioritizedList), }, - controllerContext.ClientBuilder.ClientOrDie("resource-claim-controller"), + client, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Resource().V1().ResourceClaims(), controllerContext.InformerFactory.Resource().V1().ResourceClaimTemplates()) if err != nil { - return nil, true, fmt.Errorf("failed to start resource claim controller: %v", err) + return nil, fmt.Errorf("failed to init resource claim controller: %w", err) } - go ephemeralController.Run(ctx, defaultResourceClaimControllerWorkers) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + ephemeralController.Run(ctx, defaultResourceClaimControllerWorkers) + }, controllerName), nil } func newEndpointsControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.EndpointsController, - aliases: []string{"endpoint"}, - initFunc: startEndpointsController, + name: names.EndpointsController, + aliases: []string{"endpoint"}, + constructor: newEndpointsController, } } -func startEndpointsController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go endpointcontroller.NewEndpointController( +func newEndpointsController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("endpoint-controller") + if err != nil { + return nil, err + } + + ec := endpointcontroller.NewEndpointController( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Services(), controllerContext.InformerFactory.Core().V1().Endpoints(), - controllerContext.ClientBuilder.ClientOrDie("endpoint-controller"), + client, controllerContext.ComponentConfig.EndpointController.EndpointUpdatesBatchPeriod.Duration, - ).Run(ctx, int(controllerContext.ComponentConfig.EndpointController.ConcurrentEndpointSyncs)) - return nil, true, nil + ) + return newControllerLoop(func(ctx context.Context) { + ec.Run(ctx, int(controllerContext.ComponentConfig.EndpointController.ConcurrentEndpointSyncs)) + }, controllerName), nil } func newReplicationControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ReplicationControllerController, - aliases: []string{"replicationcontroller"}, - initFunc: startReplicationController, + name: names.ReplicationControllerController, + aliases: []string{"replicationcontroller"}, + constructor: newReplicationController, } } -func startReplicationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go replicationcontroller.NewReplicationManager( +func newReplicationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("replication-controller") + if err != nil { + return nil, err + } + + rc := replicationcontroller.NewReplicationManager( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().ReplicationControllers(), - controllerContext.ClientBuilder.ClientOrDie("replication-controller"), + client, replicationcontroller.BurstReplicas, - ).Run(ctx, int(controllerContext.ComponentConfig.ReplicationController.ConcurrentRCSyncs)) - return nil, true, nil + ) + + return newControllerLoop(func(ctx context.Context) { + rc.Run(ctx, int(controllerContext.ComponentConfig.ReplicationController.ConcurrentRCSyncs)) + }, controllerName), nil } func newPodGarbageCollectorControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PodGarbageCollectorController, - aliases: []string{"podgc"}, - initFunc: startPodGarbageCollectorController, + name: names.PodGarbageCollectorController, + aliases: []string{"podgc"}, + constructor: newPodGarbageCollectorController, } } -func startPodGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go podgc.NewPodGC( +func newPodGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("pod-garbage-collector") + if err != nil { + return nil, err + } + + pgcc := podgc.NewPodGC( ctx, - controllerContext.ClientBuilder.ClientOrDie("pod-garbage-collector"), + client, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Nodes(), int(controllerContext.ComponentConfig.PodGCController.TerminatedPodGCThreshold), - ).Run(ctx) - return nil, true, nil + ) + return newControllerLoop(pgcc.Run, controllerName), nil } func newResourceQuotaControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ResourceQuotaController, - aliases: []string{"resourcequota"}, - initFunc: startResourceQuotaController, + name: names.ResourceQuotaController, + aliases: []string{"resourcequota"}, + constructor: newResourceQuotaController, } } -func startResourceQuotaController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - resourceQuotaControllerClient := controllerContext.ClientBuilder.ClientOrDie("resourcequota-controller") - resourceQuotaControllerDiscoveryClient := controllerContext.ClientBuilder.DiscoveryClientOrDie("resourcequota-controller") +func newResourceQuotaController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + resourceQuotaControllerClient, err := controllerContext.NewClient("resourcequota-controller") + if err != nil { + return nil, err + } + + resourceQuotaControllerDiscoveryClient, err := controllerContext.ClientBuilder.DiscoveryClient("resourcequota-controller") + if err != nil { + return nil, fmt.Errorf("failed to create the discovery client: %w", err) + } + discoveryFunc := resourceQuotaControllerDiscoveryClient.ServerPreferredNamespacedResources listerFuncForResource := generic.ListerFuncForResourceFunc(controllerContext.InformerFactory.ForResource) - quotaConfiguration := quotainstall.NewQuotaConfigurationForControllers(listerFuncForResource) + quotaConfiguration := quotainstall.NewQuotaConfigurationForControllers(listerFuncForResource, controllerContext.InformerFactory) resourceQuotaControllerOptions := &resourcequotacontroller.ControllerOptions{ QuotaClient: resourceQuotaControllerClient.CoreV1(), @@ -541,40 +613,54 @@ func startResourceQuotaController(ctx context.Context, controllerContext Control } resourceQuotaController, err := resourcequotacontroller.NewController(ctx, resourceQuotaControllerOptions) if err != nil { - return nil, false, err + return nil, err } - go resourceQuotaController.Run(ctx, int(controllerContext.ComponentConfig.ResourceQuotaController.ConcurrentResourceQuotaSyncs)) - // Periodically the quota controller to detect new resource types - go resourceQuotaController.Sync(ctx, discoveryFunc, 30*time.Second) - - return nil, true, nil + return newControllerLoop(concurrentRun( + func(ctx context.Context) { + resourceQuotaController.Run(ctx, int(controllerContext.ComponentConfig.ResourceQuotaController.ConcurrentResourceQuotaSyncs)) + }, + func(ctx context.Context) { + resourceQuotaController.Sync(ctx, discoveryFunc, 30*time.Second) + }, + ), controllerName), nil } func newNamespaceControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.NamespaceController, - aliases: []string{"namespace"}, - initFunc: startNamespaceController, + name: names.NamespaceController, + aliases: []string{"namespace"}, + constructor: newNamespaceController, } } -func startNamespaceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newNamespaceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { // the namespace cleanup controller is very chatty. It makes lots of discovery calls and then it makes lots of delete calls // the ratelimiter negatively affects its speed. Deleting 100 total items in a namespace (that's only a few of each resource // including events), takes ~10 seconds by default. - nsKubeconfig := controllerContext.ClientBuilder.ConfigOrDie("namespace-controller") + nsKubeconfig, err := controllerContext.NewClientConfig("namespace-controller") + if err != nil { + return nil, err + } + nsKubeconfig.QPS *= 20 nsKubeconfig.Burst *= 100 - namespaceKubeClient := clientset.NewForConfigOrDie(nsKubeconfig) - return startModifiedNamespaceController(ctx, controllerContext, namespaceKubeClient, nsKubeconfig) -} -func startModifiedNamespaceController(ctx context.Context, controllerContext ControllerContext, namespaceKubeClient clientset.Interface, nsKubeconfig *restclient.Config) (controller.Interface, bool, error) { + namespaceKubeClient, err := clientset.NewForConfig(nsKubeconfig) + if err != nil { + return nil, err + } + + return newModifiedNamespaceController(ctx, controllerContext, controllerName, namespaceKubeClient, nsKubeconfig) +} +func newModifiedNamespaceController( + ctx context.Context, controllerContext ControllerContext, controllerName string, + namespaceKubeClient clientset.Interface, nsKubeconfig *restclient.Config, +) (Controller, error) { metadataClient, err := metadata.NewForConfig(nsKubeconfig) if err != nil { - return nil, true, err + return nil, err } discoverResourcesFn := namespaceKubeClient.Discovery().ServerPreferredNamespacedResources @@ -588,204 +674,291 @@ func startModifiedNamespaceController(ctx context.Context, controllerContext Con controllerContext.ComponentConfig.NamespaceController.NamespaceSyncPeriod.Duration, v1.FinalizerKubernetes, ) - go namespaceController.Run(ctx, int(controllerContext.ComponentConfig.NamespaceController.ConcurrentNamespaceSyncs)) - - return nil, true, nil + return newControllerLoop(func(ctx context.Context) { + namespaceController.Run(ctx, int(controllerContext.ComponentConfig.NamespaceController.ConcurrentNamespaceSyncs)) + }, controllerName), nil } func newServiceAccountControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ServiceAccountController, - aliases: []string{"serviceaccount"}, - initFunc: startServiceAccountController, + name: names.ServiceAccountController, + aliases: []string{"serviceaccount"}, + constructor: newServiceAccountController, } } -func startServiceAccountController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newServiceAccountController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("service-account-controller") + if err != nil { + return nil, err + } + logger := klog.FromContext(ctx) + sac, err := serviceaccountcontroller.NewServiceAccountsController( + logger, controllerContext.InformerFactory.Core().V1().ServiceAccounts(), controllerContext.InformerFactory.Core().V1().Namespaces(), - controllerContext.ClientBuilder.ClientOrDie("service-account-controller"), + client, serviceaccountcontroller.DefaultServiceAccountsControllerOptions(), ) if err != nil { - return nil, true, fmt.Errorf("error creating ServiceAccount controller: %v", err) + return nil, fmt.Errorf("error creating ServiceAccount controller: %w", err) } - go sac.Run(ctx, 1) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + sac.Run(ctx, 1) + }, controllerName), nil } func newTTLControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.TTLController, - aliases: []string{"ttl"}, - initFunc: startTTLController, + name: names.TTLController, + aliases: []string{"ttl"}, + constructor: newTTLController, } } -func startTTLController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go ttlcontroller.NewTTLController( +func newTTLController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("ttl-controller") + if err != nil { + return nil, err + } + + ttlc := ttlcontroller.NewTTLController( ctx, controllerContext.InformerFactory.Core().V1().Nodes(), - controllerContext.ClientBuilder.ClientOrDie("ttl-controller"), - ).Run(ctx, 5) - return nil, true, nil + client, + ) + return newControllerLoop(func(ctx context.Context) { + ttlc.Run(ctx, 5) + }, controllerName), nil } func newGarbageCollectorControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.GarbageCollectorController, - aliases: []string{"garbagecollector"}, - initFunc: startGarbageCollectorController, + name: names.GarbageCollectorController, + aliases: []string{"garbagecollector"}, + constructor: newGarbageCollectorController, } } -func startGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +type garbageCollectorController struct { + *garbagecollector.GarbageCollector + controllerContext ControllerContext + controllerName string + discoveryClient discovery.DiscoveryInterface +} + +// Make sure we are propagating properly. +var _ controller.Debuggable = (*garbageCollectorController)(nil) + +func newGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { if !controllerContext.ComponentConfig.GarbageCollectorController.EnableGarbageCollector { - return nil, false, nil + return nil, nil + } + + client, err := controllerContext.NewClient("generic-garbage-collector") + if err != nil { + return nil, err } - gcClientset := controllerContext.ClientBuilder.ClientOrDie("generic-garbage-collector") - discoveryClient := controllerContext.ClientBuilder.DiscoveryClientOrDie("generic-garbage-collector") + discoveryClient, err := controllerContext.ClientBuilder.DiscoveryClient("generic-garbage-collector") + if err != nil { + return nil, fmt.Errorf("failed to create the discovery client: %w", err) + } + + config, err := controllerContext.NewClientConfig("generic-garbage-collector") + if err != nil { + return nil, err + } - config := controllerContext.ClientBuilder.ConfigOrDie("generic-garbage-collector") // Increase garbage collector controller's throughput: each object deletion takes two API calls, // so to get |config.QPS| deletion rate we need to allow 2x more requests for this controller. config.QPS *= 2 metadataClient, err := metadata.NewForConfig(config) if err != nil { - return nil, true, err + return nil, err } garbageCollector, err := garbagecollector.NewComposedGarbageCollector( ctx, - gcClientset, + client, metadataClient, controllerContext.RESTMapper, controllerContext.GraphBuilder, ) if err != nil { - return nil, true, fmt.Errorf("failed to start the generic garbage collector: %w", err) + return nil, fmt.Errorf("failed to init the generic garbage collector: %w", err) } - // Start the garbage collector. - workers := int(controllerContext.ComponentConfig.GarbageCollectorController.ConcurrentGCSyncs) - const syncPeriod = 30 * time.Second - go garbageCollector.Run(ctx, workers, syncPeriod) + return &garbageCollectorController{ + GarbageCollector: garbageCollector, + controllerName: controllerName, + controllerContext: controllerContext, + discoveryClient: discoveryClient, + }, nil +} - // Periodically refresh the RESTMapper with new discovery information and sync - // the garbage collector. - go garbageCollector.Sync(ctx, discoveryClient, syncPeriod) +// Name must be implemented explicitly as it collides with the embedded controller. +func (c *garbageCollectorController) Name() string { + return c.controllerName +} - return garbageCollector, true, nil +func (c *garbageCollectorController) Run(ctx context.Context) { + workers := int(c.controllerContext.ComponentConfig.GarbageCollectorController.ConcurrentGCSyncs) + const syncPeriod = 30 * time.Second + + concurrentRun( + func(ctx context.Context) { + c.GarbageCollector.Run(ctx, workers, syncPeriod) + }, + func(ctx context.Context) { + // Periodically refresh the RESTMapper with new discovery information and sync the garbage collector. + c.Sync(ctx, c.discoveryClient, syncPeriod) + }, + )(ctx) } func newPersistentVolumeClaimProtectionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PersistentVolumeClaimProtectionController, - aliases: []string{"pvc-protection"}, - initFunc: startPersistentVolumeClaimProtectionController, + name: names.PersistentVolumeClaimProtectionController, + aliases: []string{"pvc-protection"}, + constructor: newPersistentVolumeClaimProtectionController, } } -func startPersistentVolumeClaimProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newPersistentVolumeClaimProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("pvc-protection-controller") + if err != nil { + return nil, err + } + pvcProtectionController, err := pvcprotection.NewPVCProtectionController( klog.FromContext(ctx), controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.ClientBuilder.ClientOrDie("pvc-protection-controller"), + client, ) if err != nil { - return nil, true, fmt.Errorf("failed to start the pvc protection controller: %v", err) + return nil, fmt.Errorf("failed to init the pvc protection controller: %w", err) } - go pvcProtectionController.Run(ctx, 1) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + pvcProtectionController.Run(ctx, 1) + }, controllerName), nil } func newPersistentVolumeProtectionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.PersistentVolumeProtectionController, - aliases: []string{"pv-protection"}, - initFunc: startPersistentVolumeProtectionController, + name: names.PersistentVolumeProtectionController, + aliases: []string{"pv-protection"}, + constructor: newPersistentVolumeProtectionController, } } -func startPersistentVolumeProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go pvprotection.NewPVProtectionController( +func newPersistentVolumeProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("pv-protection-controller") + if err != nil { + return nil, err + } + + pvpc := pvprotection.NewPVProtectionController( klog.FromContext(ctx), controllerContext.InformerFactory.Core().V1().PersistentVolumes(), - controllerContext.ClientBuilder.ClientOrDie("pv-protection-controller"), - ).Run(ctx, 1) - return nil, true, nil + client, + ) + return newControllerLoop(func(ctx context.Context) { + pvpc.Run(ctx, 1) + }, controllerName), nil } func newVolumeAttributesClassProtectionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.VolumeAttributesClassProtectionController, - initFunc: startVolumeAttributesClassProtectionController, + name: names.VolumeAttributesClassProtectionController, + constructor: newVolumeAttributesClassProtectionController, requiredFeatureGates: []featuregate.Feature{ features.VolumeAttributesClass, }, } } -func startVolumeAttributesClassProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newVolumeAttributesClassProtectionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("volumeattributesclass-protection-controller") + if err != nil { + return nil, err + } + vacProtectionController, err := vacprotection.NewVACProtectionController( klog.FromContext(ctx), - controllerContext.ClientBuilder.ClientOrDie("volumeattributesclass-protection-controller"), + client, controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), controllerContext.InformerFactory.Core().V1().PersistentVolumes(), controllerContext.InformerFactory.Storage().V1().VolumeAttributesClasses(), ) if err != nil { - return nil, true, fmt.Errorf("failed to start the vac protection controller: %w", err) + return nil, fmt.Errorf("failed to init the vac protection controller: %w", err) } - go vacProtectionController.Run(ctx, 1) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + vacProtectionController.Run(ctx, 1) + }, controllerName), nil } func newTTLAfterFinishedControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.TTLAfterFinishedController, - aliases: []string{"ttl-after-finished"}, - initFunc: startTTLAfterFinishedController, + name: names.TTLAfterFinishedController, + aliases: []string{"ttl-after-finished"}, + constructor: newTTLAfterFinishedController, } } -func startTTLAfterFinishedController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go ttlafterfinished.New( +func newTTLAfterFinishedController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("ttl-after-finished-controller") + if err != nil { + return nil, err + } + + ttlc := ttlafterfinished.New( ctx, controllerContext.InformerFactory.Batch().V1().Jobs(), - controllerContext.ClientBuilder.ClientOrDie("ttl-after-finished-controller"), - ).Run(ctx, int(controllerContext.ComponentConfig.TTLAfterFinishedController.ConcurrentTTLSyncs)) - return nil, true, nil + client, + ) + return newControllerLoop(func(ctx context.Context) { + ttlc.Run(ctx, int(controllerContext.ComponentConfig.TTLAfterFinishedController.ConcurrentTTLSyncs)) + }, controllerName), nil } func newLegacyServiceAccountTokenCleanerControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.LegacyServiceAccountTokenCleanerController, - aliases: []string{"legacy-service-account-token-cleaner"}, - initFunc: startLegacyServiceAccountTokenCleanerController, + name: names.LegacyServiceAccountTokenCleanerController, + aliases: []string{"legacy-service-account-token-cleaner"}, + constructor: newLegacyServiceAccountTokenCleanerController, } } -func startLegacyServiceAccountTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { +func newLegacyServiceAccountTokenCleanerController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("legacy-service-account-token-cleaner") + if err != nil { + return nil, err + } + cleanUpPeriod := controllerContext.ComponentConfig.LegacySATokenCleaner.CleanUpPeriod.Duration legacySATokenCleaner, err := serviceaccountcontroller.NewLegacySATokenCleaner( controllerContext.InformerFactory.Core().V1().ServiceAccounts(), controllerContext.InformerFactory.Core().V1().Secrets(), controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.ClientBuilder.ClientOrDie("legacy-service-account-token-cleaner"), + client, clock.RealClock{}, serviceaccountcontroller.LegacySATokenCleanerOptions{ CleanUpPeriod: cleanUpPeriod, SyncInterval: serviceaccountcontroller.DefaultCleanerSyncInterval, - }) + }, + ) if err != nil { - return nil, true, fmt.Errorf("failed to start the legacy service account token cleaner: %v", err) + return nil, fmt.Errorf("failed to init the legacy service account token cleaner: %w", err) } - go legacySATokenCleaner.Run(ctx) - return nil, true, nil + + return newControllerLoop(legacySATokenCleaner.Run, controllerName), nil } // processCIDRs is a helper function that works on a comma separated cidrs and returns @@ -906,9 +1079,9 @@ func setNodeCIDRMaskSizes(cfg nodeipamconfig.NodeIPAMControllerConfiguration, cl func newStorageVersionGarbageCollectorControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.StorageVersionGarbageCollectorController, - aliases: []string{"storage-version-gc"}, - initFunc: startStorageVersionGarbageCollectorController, + name: names.StorageVersionGarbageCollectorController, + aliases: []string{"storage-version-gc"}, + constructor: newStorageVersionGarbageCollectorController, requiredFeatureGates: []featuregate.Feature{ genericfeatures.APIServerIdentity, genericfeatures.StorageVersionAPI, @@ -916,20 +1089,25 @@ func newStorageVersionGarbageCollectorControllerDescriptor() *ControllerDescript } } -func startStorageVersionGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go storageversiongc.NewStorageVersionGC( +func newStorageVersionGarbageCollectorController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("storage-version-garbage-collector") + if err != nil { + return nil, err + } + + svgcc := storageversiongc.NewStorageVersionGC( ctx, - controllerContext.ClientBuilder.ClientOrDie("storage-version-garbage-collector"), + client, controllerContext.InformerFactory.Coordination().V1().Leases(), controllerContext.InformerFactory.Internal().V1alpha1().StorageVersions(), - ).Run(ctx) - return nil, true, nil + ) + return newControllerLoop(svgcc.Run, controllerName), nil } func newSELinuxWarningControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ name: names.SELinuxWarningController, - initFunc: startSELinuxWarningController, + constructor: newSELinuxWarningController, isDisabledByDefault: true, requiredFeatureGates: []featuregate.Feature{ features.SELinuxChangePolicy, @@ -937,32 +1115,34 @@ func newSELinuxWarningControllerDescriptor() *ControllerDescriptor { } } -func startSELinuxWarningController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - if !utilfeature.DefaultFeatureGate.Enabled(features.SELinuxChangePolicy) { - return nil, false, nil +func newSELinuxWarningController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient(controllerName) + if err != nil { + return nil, err } logger := klog.FromContext(ctx) csiDriverInformer := controllerContext.InformerFactory.Storage().V1().CSIDrivers() plugins, err := ProbePersistentVolumePlugins(logger, controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration) if err != nil { - return nil, true, fmt.Errorf("failed to probe volume plugins when starting SELinux warning controller: %w", err) + return nil, fmt.Errorf("failed to probe volume plugins when starting SELinux warning controller: %w", err) } - seLinuxController, err := - selinuxwarning.NewController( - ctx, - controllerContext.ClientBuilder.ClientOrDie(controllerName), - controllerContext.InformerFactory.Core().V1().Pods(), - controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), - controllerContext.InformerFactory.Core().V1().PersistentVolumes(), - csiDriverInformer, - plugins, - GetDynamicPluginProber(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration), - ) + seLinuxController, err := selinuxwarning.NewController( + ctx, + client, + controllerContext.InformerFactory.Core().V1().Pods(), + controllerContext.InformerFactory.Core().V1().PersistentVolumeClaims(), + controllerContext.InformerFactory.Core().V1().PersistentVolumes(), + csiDriverInformer, + plugins, + GetDynamicPluginProber(controllerContext.ComponentConfig.PersistentVolumeBinderController.VolumeConfiguration), + ) if err != nil { - return nil, true, fmt.Errorf("failed to start SELinux warning controller: %w", err) + return nil, fmt.Errorf("failed to start SELinux warning controller: %w", err) } - go seLinuxController.Run(ctx, 1) - return nil, true, nil + + return newControllerLoop(func(ctx context.Context) { + seLinuxController.Run(ctx, 1) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/core_test.go b/cmd/kube-controller-manager/app/core_test.go index d66a768df8940..8808ce6bf811e 100644 --- a/cmd/kube-controller-manager/app/core_test.go +++ b/cmd/kube-controller-manager/app/core_test.go @@ -18,6 +18,7 @@ package app import ( "context" + "sync" "testing" "time" @@ -28,6 +29,8 @@ import ( clientset "k8s.io/client-go/kubernetes" fakeclientset "k8s.io/client-go/kubernetes/fake" restclient "k8s.io/client-go/rest" + + "k8s.io/kubernetes/cmd/kube-controller-manager/names" ) // TestClientBuilder inherits ClientBuilder and can accept a given fake clientset. @@ -35,12 +38,14 @@ type TestClientBuilder struct { clientset clientset.Interface } -func (TestClientBuilder) Config(name string) (*restclient.Config, error) { return nil, nil } +func (TestClientBuilder) Config(name string) (*restclient.Config, error) { + return &restclient.Config{}, nil +} func (TestClientBuilder) ConfigOrDie(name string) *restclient.Config { return &restclient.Config{} } -func (TestClientBuilder) Client(name string) (clientset.Interface, error) { return nil, nil } +func (m TestClientBuilder) Client(name string) (clientset.Interface, error) { return m.clientset, nil } func (m TestClientBuilder) ClientOrDie(name string) clientset.Interface { return m.clientset } @@ -130,26 +135,44 @@ func TestController_DiscoveryError(t *testing.T) { }, } for name, test := range tcs { - testDiscovery := FakeDiscoveryWithError{Err: test.discoveryError, PossibleResources: test.possibleResources} - testClientset := NewFakeClientset(testDiscovery) - testClientBuilder := TestClientBuilder{clientset: testClientset} - testInformerFactory := informers.NewSharedInformerFactoryWithOptions(testClientset, time.Duration(1)) - ctx := ControllerContext{ - ClientBuilder: testClientBuilder, - InformerFactory: testInformerFactory, - ObjectOrMetadataInformerFactory: testInformerFactory, - InformersStarted: make(chan struct{}), - } - for controllerName, controllerDesc := range controllerDescriptorMap { - _, _, err := controllerDesc.GetInitFunc()(context.TODO(), ctx, controllerName) + t.Run(name, func(t *testing.T) { + ctx := context.Background() + testDiscovery := FakeDiscoveryWithError{Err: test.discoveryError, PossibleResources: test.possibleResources} + testClientset := NewFakeClientset(testDiscovery) + testClientBuilder := TestClientBuilder{clientset: testClientset} + testInformerFactory := informers.NewSharedInformerFactoryWithOptions(testClientset, time.Duration(1)) + controllerContext := ControllerContext{ + ClientBuilder: testClientBuilder, + InformerFactory: testInformerFactory, + ObjectOrMetadataInformerFactory: testInformerFactory, + InformersStarted: make(chan struct{}), + } + for controllerName, controllerDesc := range controllerDescriptorMap { + _, err := controllerDesc.GetControllerConstructor()(ctx, controllerContext, controllerName) + if test.expectedErr != (err != nil) { + t.Errorf("%v test failed for use case: %v", controllerName, name) + } + } + + namespaceController, err := newModifiedNamespaceController( + ctx, controllerContext, names.NamespaceController, + testClientset, testClientBuilder.ConfigOrDie("namespace-controller")) + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithCancel(ctx) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + namespaceController.Run(ctx) + }() + cancel() + wg.Wait() if test.expectedErr != (err != nil) { - t.Errorf("%v test failed for use case: %v", controllerName, name) + t.Errorf("Namespace Controller test failed for use case: %v", name) } - } - _, _, err := startModifiedNamespaceController( - context.TODO(), ctx, testClientset, testClientBuilder.ConfigOrDie("namespace-controller")) - if test.expectedErr != (err != nil) { - t.Errorf("Namespace Controller test failed for use case: %v", name) - } + }) } } diff --git a/cmd/kube-controller-manager/app/discovery.go b/cmd/kube-controller-manager/app/discovery.go index e79fc9b2b932c..f749cc58e6cd8 100644 --- a/cmd/kube-controller-manager/app/discovery.go +++ b/cmd/kube-controller-manager/app/discovery.go @@ -22,7 +22,6 @@ package app import ( "context" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" endpointslicecontroller "k8s.io/kubernetes/pkg/controller/endpointslice" endpointslicemirroringcontroller "k8s.io/kubernetes/pkg/controller/endpointslicemirroring" @@ -30,43 +29,57 @@ import ( func newEndpointSliceControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.EndpointSliceController, - aliases: []string{"endpointslice"}, - initFunc: startEndpointSliceController, + name: names.EndpointSliceController, + aliases: []string{"endpointslice"}, + constructor: newEndpointSliceController, } } -func startEndpointSliceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go endpointslicecontroller.NewController( +func newEndpointSliceController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("endpointslice-controller") + if err != nil { + return nil, err + } + + esc := endpointslicecontroller.NewController( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Core().V1().Services(), controllerContext.InformerFactory.Core().V1().Nodes(), controllerContext.InformerFactory.Discovery().V1().EndpointSlices(), controllerContext.ComponentConfig.EndpointSliceController.MaxEndpointsPerSlice, - controllerContext.ClientBuilder.ClientOrDie("endpointslice-controller"), + client, controllerContext.ComponentConfig.EndpointSliceController.EndpointUpdatesBatchPeriod.Duration, - ).Run(ctx, int(controllerContext.ComponentConfig.EndpointSliceController.ConcurrentServiceEndpointSyncs)) - return nil, true, nil + ) + return newControllerLoop(func(ctx context.Context) { + esc.Run(ctx, int(controllerContext.ComponentConfig.EndpointSliceController.ConcurrentServiceEndpointSyncs)) + }, controllerName), nil } func newEndpointSliceMirroringControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.EndpointSliceMirroringController, - aliases: []string{"endpointslicemirroring"}, - initFunc: startEndpointSliceMirroringController, + name: names.EndpointSliceMirroringController, + aliases: []string{"endpointslicemirroring"}, + constructor: newEndpointSliceMirroringController, } } -func startEndpointSliceMirroringController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go endpointslicemirroringcontroller.NewController( +func newEndpointSliceMirroringController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("endpointslicemirroring-controller") + if err != nil { + return nil, err + } + + esmc := endpointslicemirroringcontroller.NewController( ctx, controllerContext.InformerFactory.Core().V1().Endpoints(), controllerContext.InformerFactory.Discovery().V1().EndpointSlices(), controllerContext.InformerFactory.Core().V1().Services(), controllerContext.ComponentConfig.EndpointSliceMirroringController.MirroringMaxEndpointsPerSubset, - controllerContext.ClientBuilder.ClientOrDie("endpointslicemirroring-controller"), + client, controllerContext.ComponentConfig.EndpointSliceMirroringController.MirroringEndpointUpdatesBatchPeriod.Duration, - ).Run(ctx, int(controllerContext.ComponentConfig.EndpointSliceMirroringController.MirroringConcurrentServiceEndpointSyncs)) - return nil, true, nil + ) + return newControllerLoop(func(ctx context.Context) { + esmc.Run(ctx, int(controllerContext.ComponentConfig.EndpointSliceMirroringController.MirroringConcurrentServiceEndpointSyncs)) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/networking.go b/cmd/kube-controller-manager/app/networking.go index aa1c3c9d1119a..ac4551d9dcd40 100644 --- a/cmd/kube-controller-manager/app/networking.go +++ b/cmd/kube-controller-manager/app/networking.go @@ -23,7 +23,6 @@ import ( "context" "k8s.io/component-base/featuregate" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/servicecidrs" "k8s.io/kubernetes/pkg/features" @@ -31,20 +30,28 @@ import ( func newServiceCIDRsControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ServiceCIDRController, - initFunc: startServiceCIDRsController, + name: names.ServiceCIDRController, + constructor: newServiceCIDRsController, requiredFeatureGates: []featuregate.Feature{ features.MultiCIDRServiceAllocator, - }} + }, + } } -func startServiceCIDRsController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go servicecidrs.NewController( + +func newServiceCIDRsController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("service-cidrs-controller") + if err != nil { + return nil, err + } + + // TODO use component config + scc := servicecidrs.NewController( ctx, controllerContext.InformerFactory.Networking().V1().ServiceCIDRs(), controllerContext.InformerFactory.Networking().V1().IPAddresses(), - controllerContext.ClientBuilder.ClientOrDie("service-cidrs-controller"), - ).Run(ctx, 5) - // TODO use component config - return nil, true, nil - + client, + ) + return newControllerLoop(func(ctx context.Context) { + scc.Run(ctx, 5) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller.go b/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller.go new file mode 100644 index 0000000000000..64bd263ed4dab --- /dev/null +++ b/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller.go @@ -0,0 +1,63 @@ +/* +Copyright 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 options + +import ( + "fmt" + + "github.com/spf13/pflag" + + devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" +) + +// DeviceTaintEvictionControllerOptions holds the DeviceTaintEvictionController options. +type DeviceTaintEvictionControllerOptions struct { + *devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration +} + +// AddFlags adds flags related to DeviceTaintEvictionController for controller manager to the specified FlagSet. +func (o *DeviceTaintEvictionControllerOptions) AddFlags(fs *pflag.FlagSet) { + if o == nil { + return + } + + fs.Int32Var(&o.ConcurrentSyncs, "concurrent-device-taint-eviction-syncs", o.ConcurrentSyncs, "The number of operations (evicting pods, updating DeviceTaintRule status) allowed to run concurrently. Greater number = more responsive, but more CPU (and network) load") +} + +// ApplyTo fills up DeviceTaintEvictionController config with options. +func (o *DeviceTaintEvictionControllerOptions) ApplyTo(cfg *devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration) error { + if o == nil { + return nil + } + + cfg.ConcurrentSyncs = o.ConcurrentSyncs + + return nil +} + +// Validate checks validation of DeviceTaintEvictionControllerOptions. +func (o *DeviceTaintEvictionControllerOptions) Validate() []error { + if o == nil { + return nil + } + + var errs []error + if o.ConcurrentSyncs <= 0 { + errs = append(errs, fmt.Errorf("concurrent-device-taint-eviction-syncs must be greater than zero, got %d", o.ConcurrentSyncs)) + } + return errs +} diff --git a/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller_test.go b/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller_test.go new file mode 100644 index 0000000000000..a804ad7675c46 --- /dev/null +++ b/cmd/kube-controller-manager/app/options/devicetaintevictioncontroller_test.go @@ -0,0 +1,219 @@ +/* +Copyright 2025 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 options + +import ( + "reflect" + "strings" + "testing" + + "github.com/spf13/pflag" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + + devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" +) + +func TestDeviceTaintEvictionControllerOptions_AddFlags(t *testing.T) { + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + opts := &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 50, + }, + } + + opts.AddFlags(fs) + + // Test that the flag was added + flag := fs.Lookup("concurrent-device-taint-eviction-syncs") + if flag == nil { + t.Error("concurrent-device-taint-eviction-syncs flag was not added") + return + } + + // Test that the flag has the correct default value + if flag.DefValue != "50" { + t.Errorf("expected default value 50, got %s", flag.DefValue) + } + + // Test flag parsing + args := []string{"--concurrent-device-taint-eviction-syncs=25"} + if err := fs.Parse(args); err != nil { + t.Errorf("failed to parse flags: %v", err) + } + + if opts.ConcurrentSyncs != 25 { + t.Errorf("expected ConcurrentSyncs to be 25, got %d", opts.ConcurrentSyncs) + } +} + +func TestDeviceTaintEvictionControllerOptions_AddFlags_Nil(t *testing.T) { + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + var opts *DeviceTaintEvictionControllerOptions + + // Should not panic when options is nil + opts.AddFlags(fs) + + // Flag should not be added + flag := fs.Lookup("concurrent-device-taint-eviction-syncs") + if flag != nil { + t.Error("concurrent-device-taint-eviction-syncs flag should not be added when options is nil") + } +} + +func TestDeviceTaintEvictionControllerOptions_ApplyTo(t *testing.T) { + opts := &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 75, + }, + } + + cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{} + + err := opts.ApplyTo(cfg) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if cfg.ConcurrentSyncs != 75 { + t.Errorf("expected ConcurrentSyncs to be 75, got %d", cfg.ConcurrentSyncs) + } +} + +func TestDeviceTaintEvictionControllerOptions_ApplyTo_Nil(t *testing.T) { + var opts *DeviceTaintEvictionControllerOptions + cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 50, + } + + err := opts.ApplyTo(cfg) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // Configuration should remain unchanged + if cfg.ConcurrentSyncs != 50 { + t.Errorf("expected ConcurrentSyncs to remain 50, got %d", cfg.ConcurrentSyncs) + } +} + +func TestDeviceTaintEvictionControllerOptions_Validate(t *testing.T) { + testCases := []struct { + name string + concurrentSyncs int32 + expectErrors bool + expectedErrorSubString string + }{ + { + name: "valid concurrent syncs", + concurrentSyncs: 50, + expectErrors: false, + }, + { + name: "valid minimum concurrent syncs", + concurrentSyncs: 1, + expectErrors: false, + }, + { + name: "invalid zero concurrent syncs", + concurrentSyncs: 0, + expectErrors: true, + expectedErrorSubString: "concurrent-device-taint-eviction-syncs must be greater than zero, got 0", + }, + { + name: "invalid negative concurrent syncs", + concurrentSyncs: -5, + expectErrors: true, + expectedErrorSubString: "concurrent-device-taint-eviction-syncs must be greater than zero, got -5", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + opts := &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: tc.concurrentSyncs, + }, + } + + errs := opts.Validate() + + if tc.expectErrors && len(errs) == 0 { + t.Error("expected validation errors, but got none") + } + + if !tc.expectErrors && len(errs) > 0 { + t.Errorf("expected no validation errors, but got: %v", errs) + } + + if tc.expectErrors && len(errs) > 0 { + gotErr := utilerrors.NewAggregate(errs).Error() + if !strings.Contains(gotErr, tc.expectedErrorSubString) { + t.Errorf("expected error to contain %q, but got %q", tc.expectedErrorSubString, gotErr) + } + } + }) + } +} + +func TestDeviceTaintEvictionControllerOptions_Validate_Nil(t *testing.T) { + var opts *DeviceTaintEvictionControllerOptions + + errs := opts.Validate() + if len(errs) != 0 { + t.Errorf("expected no validation errors for nil options, but got: %v", errs) + } +} + +func TestDeviceTaintEvictionControllerOptions_Integration(t *testing.T) { + // Test the complete workflow: create options, set flags, apply to config + fs := pflag.NewFlagSet("test", pflag.ContinueOnError) + opts := &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 50, + }, + } + + // Add flags + opts.AddFlags(fs) + + // Parse flags with custom value + args := []string{"--concurrent-device-taint-eviction-syncs=100"} + if err := fs.Parse(args); err != nil { + t.Fatalf("failed to parse flags: %v", err) + } + + // Validate + errs := opts.Validate() + if len(errs) > 0 { + t.Fatalf("validation failed: %v", errs) + } + + // Apply to config + cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{} + if err := opts.ApplyTo(cfg); err != nil { + t.Fatalf("failed to apply options: %v", err) + } + + // Verify final configuration + expected := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 100, + } + + if !reflect.DeepEqual(cfg, expected) { + t.Errorf("expected config %+v, got %+v", expected, cfg) + } +} diff --git a/cmd/kube-controller-manager/app/options/options.go b/cmd/kube-controller-manager/app/options/options.go index 08750ef76db8f..2a1e8000b12a9 100644 --- a/cmd/kube-controller-manager/app/options/options.go +++ b/cmd/kube-controller-manager/app/options/options.go @@ -21,11 +21,13 @@ import ( "context" "fmt" "net" + "time" v1 "k8s.io/api/core/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/version" + "k8s.io/apiserver/pkg/server/flagz" apiserveroptions "k8s.io/apiserver/pkg/server/options" "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -44,7 +46,6 @@ import ( "k8s.io/component-base/logs" logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics" - "k8s.io/component-base/zpages/flagz" cmoptions "k8s.io/controller-manager/options" kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1" kubecontrollerconfig "k8s.io/kubernetes/cmd/kube-controller-manager/app/config" @@ -78,6 +79,7 @@ type KubeControllerManagerOptions struct { CSRSigningController *CSRSigningControllerOptions DaemonSetController *DaemonSetControllerOptions DeploymentController *DeploymentControllerOptions + DeviceTaintEvictionController *DeviceTaintEvictionControllerOptions StatefulSetController *StatefulSetControllerOptions DeprecatedFlags *DeprecatedControllerOptions EndpointController *EndpointControllerOptions @@ -101,7 +103,7 @@ type KubeControllerManagerOptions struct { TTLAfterFinishedController *TTLAfterFinishedControllerOptions ValidatingAdmissionPolicyStatusController *ValidatingAdmissionPolicyStatusControllerOptions - SecureServing *apiserveroptions.SecureServingOptionsWithLoopback + SecureServing *apiserveroptions.SecureServingOptions Authentication *apiserveroptions.DelegatingAuthenticationOptions Authorization *apiserveroptions.DelegatingAuthorizationOptions Metrics *metrics.Options @@ -110,6 +112,8 @@ type KubeControllerManagerOptions struct { Master string ShowHiddenMetricsForVersion string + ControllerShutdownTimeout time.Duration + // ComponentGlobalsRegistry is the registry where the effective versions and feature gates for all components are stored. ComponentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry @@ -152,6 +156,9 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { DeploymentController: &DeploymentControllerOptions{ &componentConfig.DeploymentController, }, + DeviceTaintEvictionController: &DeviceTaintEvictionControllerOptions{ + &componentConfig.DeviceTaintEvictionController, + }, StatefulSetController: &StatefulSetControllerOptions{ &componentConfig.StatefulSetController, }, @@ -218,7 +225,7 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { ValidatingAdmissionPolicyStatusController: &ValidatingAdmissionPolicyStatusControllerOptions{ &componentConfig.ValidatingAdmissionPolicyStatusController, }, - SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(), + SecureServing: apiserveroptions.NewSecureServingOptions(), Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(), Metrics: metrics.NewOptions(), @@ -242,6 +249,8 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) { s.GarbageCollectorController.GCIgnoredResources = gcIgnoredResources s.Generic.LeaderElection.ResourceName = "kube-controller-manager" s.Generic.LeaderElection.ResourceNamespace = "kube-system" + + s.ControllerShutdownTimeout = 10 * time.Second return &s, nil } @@ -271,6 +280,7 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy s.AttachDetachController.AddFlags(fss.FlagSet(names.PersistentVolumeAttachDetachController)) s.CSRSigningController.AddFlags(fss.FlagSet(names.CertificateSigningRequestSigningController)) s.DeploymentController.AddFlags(fss.FlagSet(names.DeploymentController)) + s.DeviceTaintEvictionController.AddFlags(fss.FlagSet(names.DeviceTaintEvictionController)) s.StatefulSetController.AddFlags(fss.FlagSet(names.StatefulSetController)) s.DaemonSetController.AddFlags(fss.FlagSet(names.DaemonSetController)) s.DeprecatedFlags.AddFlags(fss.FlagSet("deprecated")) @@ -302,6 +312,9 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).") fs.StringVar(&s.Generic.ClientConnection.Kubeconfig, "kubeconfig", s.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).") + fss.FlagSet("generic").DurationVar(&s.ControllerShutdownTimeout, "controller-shutdown-timeout", + s.ControllerShutdownTimeout, "Time to wait for the controllers to shut down before terminating the executable") + if !utilfeature.DefaultFeatureGate.Enabled(featuregate.Feature(clientgofeaturegate.WatchListClient)) { ver := version.MustParse("1.34") if err := utilfeature.DefaultMutableFeatureGate.OverrideDefaultAtVersion(featuregate.Feature(clientgofeaturegate.WatchListClient), true, ver); err != nil { @@ -340,6 +353,9 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config, a if err := s.DeploymentController.ApplyTo(&c.ComponentConfig.DeploymentController); err != nil { return err } + if err := s.DeviceTaintEvictionController.ApplyTo(&c.ComponentConfig.DeviceTaintEvictionController); err != nil { + return err + } if err := s.StatefulSetController.ApplyTo(&c.ComponentConfig.StatefulSetController); err != nil { return err } @@ -409,7 +425,7 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config, a if err := s.ValidatingAdmissionPolicyStatusController.ApplyTo(&c.ComponentConfig.ValidatingAdmissionPolicyStatusController); err != nil { return err } - if err := s.SecureServing.ApplyTo(&c.SecureServing, &c.LoopbackClientConfig); err != nil { + if err := s.SecureServing.ApplyTo(&c.SecureServing); err != nil { return err } if s.SecureServing.BindPort != 0 || s.SecureServing.Listener != nil { @@ -420,6 +436,7 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config, a return err } } + c.ControllerShutdownTimeout = s.ControllerShutdownTimeout c.OpenShiftContext = s.OpenShiftContext @@ -441,6 +458,7 @@ func (s *KubeControllerManagerOptions) Validate(allControllers []string, disable errs = append(errs, s.CSRSigningController.Validate()...) errs = append(errs, s.DaemonSetController.Validate()...) errs = append(errs, s.DeploymentController.Validate()...) + errs = append(errs, s.DeviceTaintEvictionController.Validate()...) errs = append(errs, s.StatefulSetController.Validate()...) errs = append(errs, s.DeprecatedFlags.Validate()...) errs = append(errs, s.EndpointController.Validate()...) @@ -520,11 +538,12 @@ func (s KubeControllerManagerOptions) Config(ctx context.Context, allControllers eventRecorder := eventBroadcaster.NewRecorder(clientgokubescheme.Scheme, v1.EventSource{Component: KubeControllerManagerUserAgent}) c := &kubecontrollerconfig.Config{ - Client: client, - Kubeconfig: kubeconfig, - EventBroadcaster: eventBroadcaster, - EventRecorder: eventRecorder, - ComponentGlobalsRegistry: s.ComponentGlobalsRegistry, + Client: client, + Kubeconfig: kubeconfig, + EventBroadcaster: eventBroadcaster, + EventRecorder: eventRecorder, + ControllerShutdownTimeout: s.ControllerShutdownTimeout, + ComponentGlobalsRegistry: s.ComponentGlobalsRegistry, } if err := s.ApplyTo(c, allControllers, disabledByDefaultControllers, controllerAliases); err != nil { return nil, err diff --git a/cmd/kube-controller-manager/app/options/options_test.go b/cmd/kube-controller-manager/app/options/options_test.go index 8d19d2292efbc..d931170083250 100644 --- a/cmd/kube-controller-manager/app/options/options_test.go +++ b/cmd/kube-controller-manager/app/options/options_test.go @@ -56,6 +56,7 @@ import ( cronjobconfig "k8s.io/kubernetes/pkg/controller/cronjob/config" daemonconfig "k8s.io/kubernetes/pkg/controller/daemon/config" deploymentconfig "k8s.io/kubernetes/pkg/controller/deployment/config" + devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" endpointconfig "k8s.io/kubernetes/pkg/controller/endpoint/config" endpointsliceconfig "k8s.io/kubernetes/pkg/controller/endpointslice/config" endpointslicemirroringconfig "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config" @@ -98,6 +99,7 @@ var args = []string{ "--cluster-signing-legacy-unknown-cert-file=/cluster-signing-legacy-unknown/cert-file", "--cluster-signing-legacy-unknown-key-file=/cluster-signing-legacy-unknown/key-file", "--concurrent-deployment-syncs=10", + "--concurrent-device-taint-eviction-syncs=10", "--concurrent-daemonset-syncs=10", "--concurrent-horizontal-pod-autoscaler-syncs=10", "--concurrent-statefulset-syncs=15", @@ -273,6 +275,11 @@ func TestAddFlags(t *testing.T) { ConcurrentDeploymentSyncs: 10, }, }, + DeviceTaintEvictionController: &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 10, + }, + }, StatefulSetController: &StatefulSetControllerOptions{ &statefulsetconfig.StatefulSetControllerConfiguration{ ConcurrentStatefulSetSyncs: 15, @@ -423,7 +430,7 @@ func TestAddFlags(t *testing.T) { PairName: "kube-controller-manager", }, HTTP2MaxStreamsPerConnection: 47, - }).WithLoopback(), + }), Authentication: &apiserveroptions.DelegatingAuthenticationOptions{ CacheTTL: 10 * time.Second, TokenRequestTimeout: 10 * time.Second, @@ -447,9 +454,10 @@ func TestAddFlags(t *testing.T) { AlwaysAllowPaths: []string{"/healthz", "/readyz", "/livez"}, // note: this does not match /healthz/ or /healthz/* AlwaysAllowGroups: []string{"system:masters"}, }, - Master: "192.168.4.20", - Metrics: &metrics.Options{}, - Logs: logs.NewOptions(), + Master: "192.168.4.20", + ControllerShutdownTimeout: 10 * time.Second, + Metrics: &metrics.Options{}, + Logs: logs.NewOptions(), // ignores comparing ComponentGlobalsRegistry in this test. ComponentGlobalsRegistry: s.ComponentGlobalsRegistry, } @@ -623,6 +631,9 @@ func TestApplyTo(t *testing.T) { DeploymentController: deploymentconfig.DeploymentControllerConfiguration{ ConcurrentDeploymentSyncs: 10, }, + DeviceTaintEvictionController: devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 10, + }, StatefulSetController: statefulsetconfig.StatefulSetControllerConfiguration{ ConcurrentStatefulSetSyncs: 15, }, @@ -722,6 +733,7 @@ func TestApplyTo(t *testing.T) { ConcurrentPolicySyncs: 9, }, }, + ControllerShutdownTimeout: 10 * time.Second, } // Sort GCIgnoredResources because it's built from a map, which means the @@ -1260,6 +1272,15 @@ func TestValidateControllersOptions(t *testing.T) { }, }, }, + { + name: "DeviceTaintEvictionControllerOptions", + expectErrors: false, + options: &DeviceTaintEvictionControllerOptions{ + &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{ + ConcurrentSyncs: 10, + }, + }, + }, { name: "DeprecatedControllerOptions", expectErrors: false, diff --git a/cmd/kube-controller-manager/app/plugins.go b/cmd/kube-controller-manager/app/plugins.go index 2c83e7e6b2a28..3ed1e35120bef 100644 --- a/cmd/kube-controller-manager/app/plugins.go +++ b/cmd/kube-controller-manager/app/plugins.go @@ -106,7 +106,7 @@ func probeControllerVolumePlugins(logger klog.Logger, config persistentvolumecon } if err := AttemptToLoadRecycler(config.PersistentVolumeRecyclerConfiguration.PodTemplateFilePathHostPath, &hostPathConfig); err != nil { logger.Error(err, "Could not create hostpath recycler pod from file", "path", config.PersistentVolumeRecyclerConfiguration.PodTemplateFilePathHostPath) - klog.FlushAndExit(klog.ExitFlushTimeout, 1) + return nil, err } allPlugins = append(allPlugins, hostpath.ProbeVolumePlugins(hostPathConfig)...) @@ -117,7 +117,7 @@ func probeControllerVolumePlugins(logger klog.Logger, config persistentvolumecon } if err := AttemptToLoadRecycler(config.PersistentVolumeRecyclerConfiguration.PodTemplateFilePathNFS, &nfsConfig); err != nil { logger.Error(err, "Could not create NFS recycler pod from file", "path", config.PersistentVolumeRecyclerConfiguration.PodTemplateFilePathNFS) - klog.FlushAndExit(klog.ExitFlushTimeout, 1) + return nil, err } allPlugins = append(allPlugins, nfs.ProbeVolumePlugins(nfsConfig)...) allPlugins = append(allPlugins, fc.ProbeVolumePlugins()...) diff --git a/cmd/kube-controller-manager/app/plugins_providers.go b/cmd/kube-controller-manager/app/plugins_providers.go index 9293c48f79da6..e76167242d36c 100644 --- a/cmd/kube-controller-manager/app/plugins_providers.go +++ b/cmd/kube-controller-manager/app/plugins_providers.go @@ -29,12 +29,10 @@ import ( type probeFn func() []volume.VolumePlugin func appendPluginBasedOnFeatureFlags(logger klog.Logger, plugins []volume.VolumePlugin, inTreePluginName string, featureGate featuregate.FeatureGate, pluginInfo pluginInfo) ([]volume.VolumePlugin, error) { - _, err := csimigration.CheckMigrationFeatureFlags(featureGate, pluginInfo.pluginMigrationFeature, pluginInfo.pluginUnregisterFeature) if err != nil { logger.Error(err, "Unexpected CSI Migration Feature Flags combination detected. CSI Migration may not take effect") - klog.FlushAndExit(klog.ExitFlushTimeout, 1) - // TODO: fail and return here once alpha only tests can set the feature flags for a plugin correctly + return nil, err } // Skip appending the in-tree plugin to the list of plugins to be probed/initialized diff --git a/cmd/kube-controller-manager/app/policy.go b/cmd/kube-controller-manager/app/policy.go index 0db44ba6e4aca..7a6b7c004e3d5 100644 --- a/cmd/kube-controller-manager/app/policy.go +++ b/cmd/kube-controller-manager/app/policy.go @@ -21,32 +21,38 @@ package app import ( "context" - "k8s.io/client-go/dynamic" "k8s.io/client-go/scale" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/disruption" ) func newDisruptionControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.DisruptionController, - aliases: []string{"disruption"}, - initFunc: startDisruptionController, + name: names.DisruptionController, + aliases: []string{"disruption"}, + constructor: newDisruptionController, } } -func startDisruptionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - client := controllerContext.ClientBuilder.ClientOrDie("disruption-controller") - config := controllerContext.ClientBuilder.ConfigOrDie("disruption-controller") +func newDisruptionController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("disruption-controller") + if err != nil { + return nil, err + } + + config, err := controllerContext.NewClientConfig("disruption-controller") + if err != nil { + return nil, err + } + scaleKindResolver := scale.NewDiscoveryScaleKindResolver(client.Discovery()) scaleClient, err := scale.NewForConfig(config, controllerContext.RESTMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) if err != nil { - return nil, false, err + return nil, err } - go disruption.NewDisruptionController( + dc := disruption.NewDisruptionController( ctx, controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Policy().V1().PodDisruptionBudgets(), @@ -58,6 +64,6 @@ func startDisruptionController(ctx context.Context, controllerContext Controller controllerContext.RESTMapper, scaleClient, client.Discovery(), - ).Run(ctx) - return nil, true, nil + ) + return newControllerLoop(dc.Run, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/rbac.go b/cmd/kube-controller-manager/app/rbac.go index c63c61987b4fe..465ddba6bbad2 100644 --- a/cmd/kube-controller-manager/app/rbac.go +++ b/cmd/kube-controller-manager/app/rbac.go @@ -19,23 +19,29 @@ package app import ( "context" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/clusterroleaggregation" ) func newClusterRoleAggregrationControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.ClusterRoleAggregationController, - aliases: []string{"clusterrole-aggregation"}, - initFunc: startClusterRoleAggregationController, + name: names.ClusterRoleAggregationController, + aliases: []string{"clusterrole-aggregation"}, + constructor: newClusterRoleAggregationController, } } -func startClusterRoleAggregationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - go clusterroleaggregation.NewClusterRoleAggregation( +func newClusterRoleAggregationController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + client, err := controllerContext.NewClient("clusterrole-aggregation-controller") + if err != nil { + return nil, err + } + + crac := clusterroleaggregation.NewClusterRoleAggregation( controllerContext.InformerFactory.Rbac().V1().ClusterRoles(), - controllerContext.ClientBuilder.ClientOrDie("clusterrole-aggregation-controller").RbacV1(), - ).Run(ctx, 5) - return nil, true, nil + client.RbacV1(), + ) + return newControllerLoop(func(ctx context.Context) { + crac.Run(ctx, 5) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/app/service_accounts.go b/cmd/kube-controller-manager/app/service_accounts.go new file mode 100644 index 0000000000000..1e843b7310273 --- /dev/null +++ b/cmd/kube-controller-manager/app/service_accounts.go @@ -0,0 +1,98 @@ +/* +Copyright 2025 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 app + +import ( + "context" + "fmt" + + "k8s.io/client-go/util/keyutil" + "k8s.io/controller-manager/pkg/clientbuilder" + "k8s.io/klog/v2" + "k8s.io/kubernetes/cmd/kube-controller-manager/names" + serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" + "k8s.io/kubernetes/pkg/serviceaccount" +) + +// serviceAccountTokenController is special because it must run first to set up permissions for other controllers. +// It cannot use the "normal" client builder, so it tracks its own. +func newServiceAccountTokenControllerDescriptor(rootClientBuilder clientbuilder.ControllerClientBuilder) *ControllerDescriptor { + return &ControllerDescriptor{ + name: names.ServiceAccountTokenController, + aliases: []string{"serviceaccount-token"}, + constructor: func(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + return newServiceAccountTokenController(ctx, controllerContext, controllerName, rootClientBuilder) + }, + // This controller is started manually before any other controller. + requiresSpecialHandling: true, + } +} + +func newServiceAccountTokenController( + ctx context.Context, controllerContext ControllerContext, controllerName string, + rootClientBuilder clientbuilder.ControllerClientBuilder, +) (Controller, error) { + if len(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile) == 0 { + klog.FromContext(ctx).Info("Controller is disabled because there is no private key", "controller", controllerName) + return nil, nil + } + + privateKey, err := keyutil.PrivateKeyFromFile(controllerContext.ComponentConfig.SAController.ServiceAccountKeyFile) + if err != nil { + return nil, fmt.Errorf("error reading key for service account token controller: %w", err) + } + + var rootCA []byte + if controllerContext.ComponentConfig.SAController.RootCAFile != "" { + if rootCA, err = readCA(controllerContext.ComponentConfig.SAController.RootCAFile); err != nil { + return nil, fmt.Errorf("error parsing root-ca-file at %s: %w", controllerContext.ComponentConfig.SAController.RootCAFile, err) + } + } else { + config, err := rootClientBuilder.Config("tokens-controller") + if err != nil { + return nil, fmt.Errorf("failed to create Kubernetes client config for %q: %w", "tokens-controller", err) + } + rootCA = config.CAData + } + + client, err := rootClientBuilder.Client("tokens-controller") + if err != nil { + return nil, fmt.Errorf("failed to create Kubernetes client for %q: %w", "tokens-controller", err) + } + + tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, privateKey) + if err != nil { + return nil, fmt.Errorf("failed to build token generator: %w", err) + } + tokenController, err := serviceaccountcontroller.NewTokensController( + klog.FromContext(ctx), + controllerContext.InformerFactory.Core().V1().ServiceAccounts(), + controllerContext.InformerFactory.Core().V1().Secrets(), + client, + applyOpenShiftServiceServingCertCA(serviceaccountcontroller.TokensControllerOptions{ + TokenGenerator: tokenGenerator, + RootCA: rootCA, + }), + ) + if err != nil { + return nil, fmt.Errorf("error creating Tokens controller: %w", err) + } + + return newControllerLoop(func(ctx context.Context) { + tokenController.Run(ctx, int(controllerContext.ComponentConfig.SAController.ConcurrentSATokenSyncs)) + }, controllerName), nil +} diff --git a/cmd/kube-controller-manager/app/storageversionmigrator.go b/cmd/kube-controller-manager/app/storageversionmigrator.go index 99cf02e09534e..42c3ea1bef166 100644 --- a/cmd/kube-controller-manager/app/storageversionmigrator.go +++ b/cmd/kube-controller-manager/app/storageversionmigrator.go @@ -20,77 +20,87 @@ import ( "context" "fmt" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" + clientgofeaturegate "k8s.io/client-go/features" "k8s.io/client-go/metadata" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" - "k8s.io/kubernetes/pkg/features" - - utilfeature "k8s.io/apiserver/pkg/util/feature" - clientgofeaturegate "k8s.io/client-go/features" svm "k8s.io/kubernetes/pkg/controller/storageversionmigrator" + "k8s.io/kubernetes/pkg/features" ) func newStorageVersionMigratorControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ - name: names.StorageVersionMigratorController, - aliases: []string{"svm"}, - initFunc: startSVMController, + name: names.StorageVersionMigratorController, + aliases: []string{"svm"}, + constructor: newSVMController, } } -func startSVMController( - ctx context.Context, - controllerContext ControllerContext, - controllerName string, -) (controller.Interface, bool, error) { +func newSVMController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { if !utilfeature.DefaultFeatureGate.Enabled(features.StorageVersionMigrator) || !clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InformerResourceVersion) { - return nil, false, nil + return nil, nil } if !controllerContext.ComponentConfig.GarbageCollectorController.EnableGarbageCollector { - return nil, true, fmt.Errorf("storage version migrator requires garbage collector") + return nil, fmt.Errorf("storage version migrator requires garbage collector") + } + + if !clientgofeaturegate.FeatureGates().Enabled(clientgofeaturegate.InOrderInformers) { + err := fmt.Errorf("storage version migrator requires the InOrderInformers feature gate to be enabled") + return nil, err } // svm controller can make a lot of requests during migration, keep it fast - config := controllerContext.ClientBuilder.ConfigOrDie(controllerName) + config, err := controllerContext.NewClientConfig(controllerName) + if err != nil { + return nil, err + } + config.QPS *= 20 config.Burst *= 100 - client := controllerContext.ClientBuilder.ClientOrDie(controllerName) - informer := controllerContext.InformerFactory.Storagemigration().V1alpha1().StorageVersionMigrations() + client, err := controllerContext.NewClient(controllerName) + if err != nil { + return nil, err + } + + informer := controllerContext.InformerFactory.Storagemigration().V1beta1().StorageVersionMigrations() dynamicClient, err := dynamic.NewForConfig(config) if err != nil { - return nil, false, err + return nil, err } discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) if err != nil { - return nil, false, err + return nil, err + } + + metaClient, err := metadata.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("failed to create metadata client for %s: %w", controllerName, err) } - go svm.NewResourceVersionController( - ctx, - client, - discoveryClient, - metadata.NewForConfigOrDie(config), - informer, - controllerContext.RESTMapper, - ).Run(ctx) - - svmController := svm.NewSVMController( - ctx, - client, - dynamicClient, - informer, - controllerName, - controllerContext.RESTMapper, - controllerContext.GraphBuilder, - ) - go svmController.Run(ctx) - - return svmController, true, nil + return newControllerLoop(concurrentRun( + svm.NewSVMController( + ctx, + client, + dynamicClient, + informer, + controllerName, + controllerContext.RESTMapper, + controllerContext.GraphBuilder, + ).Run, + svm.NewResourceVersionController( + ctx, + client, + discoveryClient, + metaClient, + informer, + controllerContext.RESTMapper, + ).Run, + ), controllerName), nil } diff --git a/cmd/kube-controller-manager/app/testing/testserver.go b/cmd/kube-controller-manager/app/testing/testserver.go index 1b94d26bbd3b2..b8d636b88b370 100644 --- a/cmd/kube-controller-manager/app/testing/testserver.go +++ b/cmd/kube-controller-manager/app/testing/testserver.go @@ -20,14 +20,18 @@ import ( "context" "fmt" "net" - "os" + "testing" "time" "github.com/spf13/pflag" "k8s.io/apimachinery/pkg/util/wait" + utilcompatibility "k8s.io/apiserver/pkg/util/compatibility" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" + "k8s.io/component-base/compatibility" + "k8s.io/component-base/featuregate" + featuregatetesting "k8s.io/component-base/featuregate/testing" logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" "k8s.io/kubernetes/cmd/kube-controller-manager/app" @@ -47,11 +51,10 @@ type TearDownFunc func() // TestServer return values supplied by kube-test-ApiServer type TestServer struct { - LoopbackClientConfig *restclient.Config // Rest client config using the magic token - Options *options.KubeControllerManagerOptions - Config *kubecontrollerconfig.Config - TearDownFn TearDownFunc // TearDown function - TmpDir string // Temp Dir used, by the apiserver + Options *options.KubeControllerManagerOptions + Config *kubecontrollerconfig.Config + TearDownFn TearDownFunc // TearDown function + TmpDir string // Temp Dir used, by the apiserver } // StartTestServer starts a kube-controller-manager. A rest client config and a tear-down func, @@ -60,7 +63,7 @@ type TestServer struct { // Note: we return a tear-down func instead of a stop channel because the later will leak temporary // files that because Golang testing's call to os.Exit will not give a stop channel go routine // enough time to remove temporary files. -func StartTestServer(ctx context.Context, customFlags []string) (result TestServer, err error) { +func StartTestServer(t *testing.T, ctx context.Context, customFlags []string) (result TestServer, err error) { logger := klog.FromContext(ctx) ctx, cancel := context.WithCancel(ctx) var errCh chan error @@ -75,9 +78,6 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ logger.Error(err, "Failed to shutdown test server cleanly") } } - if len(result.TmpDir) != 0 { - os.RemoveAll(result.TmpDir) - } } defer func() { if result.TearDownFn == nil { @@ -85,10 +85,7 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } }() - result.TmpDir, err = os.MkdirTemp("", "kube-controller-manager") - if err != nil { - return result, fmt.Errorf("failed to create temp dir: %v", err) - } + result.TmpDir = t.TempDir() fs := pflag.NewFlagSet("test", pflag.PanicOnError) @@ -96,6 +93,17 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ if err != nil { return TestServer{}, err } + // set up new instance of ComponentGlobalsRegistry instead of using the DefaultComponentGlobalsRegistry to avoid contention in parallel tests. + featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy() + effectiveVersion := utilcompatibility.DefaultKubeEffectiveVersionForTest() + effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) + effectiveVersion.SetMinCompatibilityVersion(featureGate.MinCompatibilityVersion()) + componentGlobalsRegistry := compatibility.NewComponentGlobalsRegistry() + if err := componentGlobalsRegistry.Register(compatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { + return result, err + } + s.ComponentGlobalsRegistry = componentGlobalsRegistry + all, disabled, aliases := app.KnownControllers(), app.ControllersDisabledByDefault(), app.ControllerAliases() namedFlagSets := s.Flags(all, disabled, aliases) for _, f := range namedFlagSets.FlagSets { @@ -104,6 +112,24 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ fs.Parse(customFlags) s.ParsedFlags = &namedFlagSets + if err := s.ComponentGlobalsRegistry.Set(); err != nil { + return result, err + } + // If the local ComponentGlobalsRegistry is changed by the flags, + // we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. + if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) || !featureGate.MinCompatibilityVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.MinCompatibilityVersion()) { + featuregatetesting.SetFeatureGateVersionsDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion(), effectiveVersion.MinCompatibilityVersion()) + } + featureOverrides := map[featuregate.Feature]bool{} + for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { + if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { + featureOverrides[f] = featureGate.Enabled(f) + } + } + if len(featureOverrides) > 0 { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featureOverrides) + } + if s.SecureServing.BindPort != 0 { s.SecureServing.Listener, s.SecureServing.BindPort, err = createListenerOnFreePort() if err != nil { @@ -130,7 +156,7 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ }(ctx) logger.Info("Waiting for /healthz to be ok...") - client, err := kubernetes.NewForConfig(config.LoopbackClientConfig) + client, err := kubernetes.NewForConfig(config.Kubeconfig) if err != nil { return result, fmt.Errorf("failed to create a client: %v", err) } @@ -156,7 +182,6 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } // from here the caller must call tearDown - result.LoopbackClientConfig = config.LoopbackClientConfig result.Options = s result.Config = config result.TearDownFn = tearDown @@ -165,13 +190,12 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } // StartTestServerOrDie calls StartTestServer t.Fatal if it does not succeed. -func StartTestServerOrDie(ctx context.Context, flags []string) *TestServer { - result, err := StartTestServer(ctx, flags) - if err == nil { - return &result +func StartTestServerOrDie(t *testing.T, ctx context.Context, flags []string) *TestServer { + result, err := StartTestServer(t, ctx, flags) + if err != nil { + t.Fatalf("failed to launch server: %v", err) } - - panic(fmt.Errorf("failed to launch server: %v", err)) + return &result } func createListenerOnFreePort() (net.Listener, int, error) { diff --git a/cmd/kube-controller-manager/app/validatingadmissionpolicystatus.go b/cmd/kube-controller-manager/app/validatingadmissionpolicystatus.go index ce6e58f693bd6..4ec1431658cf3 100644 --- a/cmd/kube-controller-manager/app/validatingadmissionpolicystatus.go +++ b/cmd/kube-controller-manager/app/validatingadmissionpolicystatus.go @@ -18,13 +18,13 @@ package app import ( "context" + "fmt" apiextensionsscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme" pluginvalidatingadmissionpolicy "k8s.io/apiserver/pkg/admission/plugin/policy/validating" "k8s.io/apiserver/pkg/cel/openapi/resolver" k8sscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/component-base/featuregate" - "k8s.io/controller-manager/controller" "k8s.io/kubernetes/cmd/kube-controller-manager/names" "k8s.io/kubernetes/pkg/controller/validatingadmissionpolicystatus" "k8s.io/kubernetes/pkg/generated/openapi" @@ -33,27 +33,40 @@ import ( func newValidatingAdmissionPolicyStatusControllerDescriptor() *ControllerDescriptor { return &ControllerDescriptor{ name: names.ValidatingAdmissionPolicyStatusController, - initFunc: startValidatingAdmissionPolicyStatusController, + constructor: newValidatingAdmissionPolicyStatusController, requiredFeatureGates: []featuregate.Feature{}, } } -func startValidatingAdmissionPolicyStatusController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { - // KCM won't start the controller without the feature gate set. +func newValidatingAdmissionPolicyStatusController(ctx context.Context, controllerContext ControllerContext, controllerName string) (Controller, error) { + discoveryClient, err := controllerContext.ClientBuilder.DiscoveryClient(names.ValidatingAdmissionPolicyStatusController) + if err != nil { + return nil, fmt.Errorf("failed to create discovery client for %s: %w", controllerName, err) + } schemaResolver := resolver.NewDefinitionsSchemaResolver(openapi.GetOpenAPIDefinitions, k8sscheme.Scheme, apiextensionsscheme.Scheme). - Combine(&resolver.ClientDiscoveryResolver{Discovery: controllerContext.ClientBuilder.DiscoveryClientOrDie(names.ValidatingAdmissionPolicyStatusController)}) + Combine(&resolver.ClientDiscoveryResolver{Discovery: discoveryClient}) typeChecker := &pluginvalidatingadmissionpolicy.TypeChecker{ SchemaResolver: schemaResolver, RestMapper: controllerContext.RESTMapper, } + + client, err := controllerContext.NewClient(names.ValidatingAdmissionPolicyStatusController) + if err != nil { + return nil, err + } + c, err := validatingadmissionpolicystatus.NewController( controllerContext.InformerFactory.Admissionregistration().V1().ValidatingAdmissionPolicies(), - controllerContext.ClientBuilder.ClientOrDie(names.ValidatingAdmissionPolicyStatusController).AdmissionregistrationV1().ValidatingAdmissionPolicies(), + client.AdmissionregistrationV1().ValidatingAdmissionPolicies(), typeChecker, ) + if err != nil { + return nil, err + } - go c.Run(ctx, int(controllerContext.ComponentConfig.ValidatingAdmissionPolicyStatusController.ConcurrentPolicySyncs)) - return nil, true, err + return newControllerLoop(func(ctx context.Context) { + c.Run(ctx, int(controllerContext.ComponentConfig.ValidatingAdmissionPolicyStatusController.ConcurrentPolicySyncs)) + }, controllerName), nil } diff --git a/cmd/kube-controller-manager/names/controller_names.go b/cmd/kube-controller-manager/names/controller_names.go index a09b055ffceb1..216a35050a2e9 100644 --- a/cmd/kube-controller-manager/names/controller_names.go +++ b/cmd/kube-controller-manager/names/controller_names.go @@ -36,7 +36,6 @@ package names // 2.1. [TODO] logging should use a canonical controller name when referencing a controller (Eg. Starting X, Shutting down X) // 2.2. [TODO] emitted events should have an EventSource.Component set to the controller name (usually when initializing an EventRecorder) // 2.3. [TODO] registering ControllerManagerMetrics with ControllerStarted and ControllerStopped -// 2.4. [TODO] calling WaitForNamedCacheSync // 3. defining controller options for "--help" command or generated documentation // 3.1. controller name should be used to create a pflag.FlagSet when registering controller options (the name is rendered in a controller flag group header) in options.KubeControllerManagerOptions // 3.2. when defined flag's help mentions a controller name diff --git a/cmd/kube-proxy/app/conntrack.go b/cmd/kube-proxy/app/conntrack.go index 8339512980798..b8a9eda6011dd 100644 --- a/cmd/kube-proxy/app/conntrack.go +++ b/cmd/kube-proxy/app/conntrack.go @@ -54,17 +54,12 @@ var errReadOnlySysFS = errors.New("readOnlySysFS") func (rct realConntracker) SetMax(ctx context.Context, max int) error { logger := klog.FromContext(ctx) + logger.Info("Setting nf_conntrack_max", "nfConntrackMax", max) if err := rct.setIntSysCtl(ctx, "nf_conntrack_max", max); err != nil { return err } - logger.Info("Setting nf_conntrack_max", "nfConntrackMax", max) - // Linux does not support writing to /sys/module/nf_conntrack/parameters/hashsize - // when the writer process is not in the initial network namespace - // (https://github.com/torvalds/linux/blob/v4.10/net/netfilter/nf_conntrack_core.c#L1795-L1796). - // Usually that's fine. But in some configurations such as with github.com/kinvolk/kubeadm-nspawn, - // kube-proxy is in another netns. - // Therefore, check if writing in hashsize is necessary and skip the writing if not. + // Check if hashsize is large enough for the nf_conntrack_max value. hashsize, err := readIntStringFile("/sys/module/nf_conntrack/parameters/hashsize") if err != nil { return err @@ -73,13 +68,12 @@ func (rct realConntracker) SetMax(ctx context.Context, max int) error { return nil } - // sysfs is expected to be mounted as 'rw'. However, it may be - // unexpectedly mounted as 'ro' by docker because of a known docker - // issue (https://github.com/docker/docker/issues/24000). Setting - // conntrack will fail when sysfs is readonly. When that happens, we - // don't set conntrack hashsize and return a special error - // errReadOnlySysFS here. The caller should deal with - // errReadOnlySysFS differently. + // sysfs is expected to be mounted as 'rw'. However, it may be unexpectedly + // mounted as 'ro' by docker because of known bugs (https://issues.k8s.io/134108). + // In that case we return a special error errReadOnlySysFS here, which the caller + // should deal with specially. + // + // TODO: this workaround can go away once we no longer support containerd 1.7. writable, err := rct.isSysFSWritable(ctx) if err != nil { return err @@ -87,7 +81,7 @@ func (rct realConntracker) SetMax(ctx context.Context, max int) error { if !writable { return errReadOnlySysFS } - // TODO: generify this and sysctl to a new sysfs.WriteInt() + logger.Info("Setting conntrack hashsize", "conntrackHashsize", max/4) return writeIntStringFile("/sys/module/nf_conntrack/parameters/hashsize", max/4) } diff --git a/cmd/kube-proxy/app/options.go b/cmd/kube-proxy/app/options.go index 0f33a0ac0bde3..ff7d349d2e9f5 100644 --- a/cmd/kube-proxy/app/options.go +++ b/cmd/kube-proxy/app/options.go @@ -28,11 +28,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apiserver/pkg/server/flagz" utilfeature "k8s.io/apiserver/pkg/util/feature" cliflag "k8s.io/component-base/cli/flag" logsapi "k8s.io/component-base/logs/api/v1" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" "k8s.io/klog/v2" "k8s.io/kube-proxy/config/v1alpha1" "k8s.io/kubernetes/pkg/cluster/ports" diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index f5771e397cf5c..308e286e8fad2 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -34,14 +34,15 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" - "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/server/flagz" "k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/routes" + "k8s.io/apiserver/pkg/server/statusz" "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" @@ -62,8 +63,6 @@ import ( "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" - "k8s.io/component-base/zpages/statusz" nodeutil "k8s.io/component-helpers/node/util" "k8s.io/klog/v2" api "k8s.io/kubernetes/pkg/apis/core" @@ -236,10 +235,10 @@ func newProxyServer(ctx context.Context, config *kubeproxyconfig.KubeProxyConfig s.Recorder = s.Broadcaster.NewRecorder(proxyconfigscheme.Scheme, kubeProxy) s.NodeRef = &v1.ObjectReference{ - Kind: "Node", - Name: s.NodeName, - UID: types.UID(s.NodeName), - Namespace: "", + APIVersion: "v1", + Kind: "Node", + Name: s.NodeName, + Namespace: "", } if len(config.HealthzBindAddress) > 0 { @@ -440,7 +439,7 @@ func serveHealthz(ctx context.Context, hz *healthcheck.ProxyHealthServer, errCh return } - fn := func() { + fn := func(ctx context.Context) { err := hz.Run(ctx) if err != nil { logger.Error(err, "Healthz server failed") @@ -454,7 +453,7 @@ func serveHealthz(ctx context.Context, hz *healthcheck.ProxyHealthServer, errCh logger.Error(nil, "Healthz server returned without error") } } - go wait.Until(fn, 5*time.Second, ctx.Done()) + go wait.UntilWithContext(ctx, fn, 5*time.Second) } func serveMetrics(ctx context.Context, bindAddress string, proxyMode kubeproxyconfig.ProxyMode, enableProfiling bool, flagzReader flagz.Reader, errCh chan error) { @@ -486,15 +485,18 @@ func serveMetrics(ctx context.Context, bindAddress string, proxyMode kubeproxyco } if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { - statusz.Install(proxyMux, kubeProxy, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion())) + reg := statusz.NewRegistry( + compatibility.DefaultBuildEffectiveVersion(), + statusz.WithListedPaths(proxyMux.ListedPaths()), + ) + statusz.Install(proxyMux, kubeProxy, reg) } - fn := func() { + fn := func(ctx context.Context) { var err error defer func() { if err != nil { - err = fmt.Errorf("starting metrics server failed: %w", err) - utilruntime.HandleError(err) + utilruntime.HandleErrorWithContext(ctx, err, "starting metrics server failed") if errCh != nil { errCh <- err // if in hardfail mode, never retry again @@ -516,7 +518,7 @@ func serveMetrics(ctx context.Context, bindAddress string, proxyMode kubeproxyco } } - go wait.Until(fn, 5*time.Second, wait.NeverStop) + go wait.UntilWithContext(ctx, fn, 5*time.Second) } // Run runs the specified ProxyServer. This should never exit (unless CleanupAndExit is set). diff --git a/cmd/kube-proxy/app/server_linux.go b/cmd/kube-proxy/app/server_linux.go index d355d23746e52..d75d597dda1cf 100644 --- a/cmd/kube-proxy/app/server_linux.go +++ b/cmd/kube-proxy/app/server_linux.go @@ -90,17 +90,21 @@ func (s *ProxyServer) platformCheckSupported(ctx context.Context) (ipv4Supported if isIPTablesBased(s.Config.Mode) { // Check for the iptables and ip6tables binaries. - ipts, errDS := utiliptables.NewDualStack() + errv4 := utiliptables.New(utiliptables.ProtocolIPv4).Present() + errv6 := utiliptables.New(utiliptables.ProtocolIPv6).Present() - ipv4Supported = ipts[v1.IPv4Protocol] != nil - ipv6Supported = ipts[v1.IPv6Protocol] != nil + ipv4Supported = errv4 == nil + ipv6Supported = errv6 == nil if !ipv4Supported && !ipv6Supported { - err = fmt.Errorf("iptables is not available on this host : %w", errDS) + // errv4 and errv6 are almost certainly the same underlying error + // ("iptables isn't installed" or "kernel modules not available") + // so it doesn't make sense to try to combine them. + err = fmt.Errorf("iptables is not available on this host : %w", errv4) } else if !ipv4Supported { - logger.Info("No iptables support for family", "ipFamily", v1.IPv4Protocol, "error", errDS) + logger.Info("No iptables support for family", "ipFamily", v1.IPv4Protocol, "error", errv4) } else if !ipv6Supported { - logger.Info("No iptables support for family", "ipFamily", v1.IPv6Protocol, "error", errDS) + logger.Info("No iptables support for family", "ipFamily", v1.IPv6Protocol, "error", errv6) } } else { // The nft CLI always supports both families. @@ -130,7 +134,7 @@ func (s *ProxyServer) createProxier(ctx context.Context, config *proxyconfigapi. if config.Mode == proxyconfigapi.ProxyModeIPTables { logger.Info("Using iptables Proxier") - ipts, _ := utiliptables.NewDualStack() + ipts := utiliptables.NewBestEffort() if dualStack { // TODO this has side effects that should only happen when Run() is invoked. @@ -184,9 +188,12 @@ func (s *ProxyServer) createProxier(ctx context.Context, config *proxyconfigapi. if err := ipvs.CanUseIPVSProxier(ctx, ipvsInterface, ipsetInterface, config.IPVS.Scheduler); err != nil { return nil, fmt.Errorf("can't use the IPVS proxier: %v", err) } - ipts, _ := utiliptables.NewDualStack() + ipts := utiliptables.NewBestEffort() logger.Info("Using ipvs Proxier") + message := "The ipvs proxier is now deprecated and may be removed in a future release. Please use 'nftables' instead." + logger.Error(nil, message) + s.Recorder.Eventf(s.NodeRef, nil, v1.EventTypeWarning, "IPVSDeprecation", "StartKubeProxy", message) if dualStack { proxier, err = ipvs.NewDualStackProxier( ctx, @@ -300,13 +307,9 @@ func (s *ProxyServer) setupConntrack(ctx context.Context, ct Conntracker) error if err != errReadOnlySysFS { return err } - // errReadOnlySysFS is caused by a known docker issue (https://github.com/docker/docker/issues/24000), - // the only remediation we know is to restart the docker daemon. - // Here we'll send an node event with specific reason and message, the - // administrator should decide whether and how to handle this issue, - // whether to drain the node and restart docker. Occurs in other container runtimes - // as well. - // TODO(random-liu): Remove this when the docker bug is fixed. + // errReadOnlySysFS means we ran into a known container runtim bug + // (https://issues.k8s.io/134108). For historical reasons we ignore + // this problem and just alert the admin that it occurred. const message = "CRI error: /sys is read-only: " + "cannot modify conntrack limits, problems may arise later (If running Docker, see docker issue #24000)" s.Recorder.Eventf(s.NodeRef, nil, v1.EventTypeWarning, err.Error(), "StartKubeProxy", message) diff --git a/cmd/kube-proxy/app/server_test.go b/cmd/kube-proxy/app/server_test.go index f95f55391a248..4c85915d91330 100644 --- a/cmd/kube-proxy/app/server_test.go +++ b/cmd/kube-proxy/app/server_test.go @@ -21,9 +21,16 @@ import ( "errors" "fmt" "net" + "net/http" + "net/http/httptest" + "strings" "testing" "time" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apiserver/pkg/server/statusz" + "k8s.io/apiserver/pkg/util/compatibility" + v1 "k8s.io/api/core/v1" kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config" "k8s.io/kubernetes/test/utils/ktesting" @@ -59,6 +66,23 @@ func (s *fakeProxyServerError) CleanupAndExit() error { return errors.New("mocking error from ProxyServer.CleanupAndExit()") } +// fakeMux matches the statusz mux interface used by statusz.Install: +// it needs Handle(path, handler) and ListedPaths(). +type fakeMux struct { + handlers map[string]http.Handler + paths []string +} + +func newFakeMux(paths []string) *fakeMux { + return &fakeMux{ + handlers: make(map[string]http.Handler), + paths: paths, + } +} + +func (m *fakeMux) Handle(path string, h http.Handler) { m.handlers[path] = h } +func (m *fakeMux) ListedPaths() []string { return m.paths } + func Test_detectNodeIPs(t *testing.T) { cases := []struct { name string @@ -565,3 +589,58 @@ func Test_checkBadIPConfig(t *testing.T) { }) } } +func TestStatuszRegistryReceivesListedPaths(t *testing.T) { + wantPaths := []string{"/livez", "/readyz", "/healthz", statusz.DefaultStatuszPath} + m := newFakeMux(wantPaths) + + reg := statusz.NewRegistry( + compatibility.DefaultBuildEffectiveVersion(), + statusz.WithListedPaths(m.ListedPaths()), + ) + statusz.Install(m, "kube-proxy", reg) + + h, ok := m.handlers[statusz.DefaultStatuszPath] + if !ok { + t.Fatalf("statusz handler not installed at %q", statusz.DefaultStatuszPath) + } + + req := httptest.NewRequest(http.MethodGet, statusz.DefaultStatuszPath, nil) + req.Header.Add("Accept", "text/plain") + rr := httptest.NewRecorder() + h.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected 200 OK, got %d; body:\n%s", rr.Code, rr.Body.String()) + } + + body := rr.Body.String() + + // Look for the "Paths" line manually instead of regex + lines := strings.Split(body, "\n") + var foundPathsLine string + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "Paths") { + foundPathsLine = line + break + } + } + if foundPathsLine == "" { + t.Fatalf("failed to find Paths line in body:\n%s", body) + } + + fields := strings.Fields(foundPathsLine) + if len(fields) < 2 { + t.Fatalf("unexpected format in Paths line: %q", foundPathsLine) + } + gotPaths := fields[1:] + + // Use sets for order-independent comparison + wantSet := sets.New[string](wantPaths...) + gotSet := sets.New[string](gotPaths...) + + if !wantSet.Equal(gotSet) { + t.Errorf("statusz listed paths mismatch.\nwant: %v\ngot: %v\nbody:\n%s", + wantSet.UnsortedList(), gotSet.UnsortedList(), body) + } +} diff --git a/cmd/kube-scheduler/app/config/config.go b/cmd/kube-scheduler/app/config/config.go index 2dbc07bdbe03f..8c91c23a60ed4 100644 --- a/cmd/kube-scheduler/app/config/config.go +++ b/cmd/kube-scheduler/app/config/config.go @@ -20,6 +20,7 @@ import ( "time" apiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/server/flagz" "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" @@ -27,7 +28,6 @@ import ( "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/leaderelection" basecompatibility "k8s.io/component-base/compatibility" - "k8s.io/component-base/zpages/flagz" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" ) @@ -39,9 +39,6 @@ type Config struct { // ComponentConfig is the scheduler server's configuration object. ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration - // LoopbackClientConfig is a config for a privileged loopback connection - LoopbackClientConfig *restclient.Config - Authentication apiserver.AuthenticationInfo Authorization apiserver.AuthorizationInfo SecureServing *apiserver.SecureServingInfo @@ -84,7 +81,5 @@ type CompletedConfig struct { func (c *Config) Complete() CompletedConfig { cc := completedConfig{c} - apiserver.AuthorizeClientBearerToken(c.LoopbackClientConfig, &c.Authentication, &c.Authorization) - return CompletedConfig{&cc} } diff --git a/cmd/kube-scheduler/app/options/options.go b/cmd/kube-scheduler/app/options/options.go index e29894dc7fd30..67bbd791c9d29 100644 --- a/cmd/kube-scheduler/app/options/options.go +++ b/cmd/kube-scheduler/app/options/options.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/apiserver/pkg/server/flagz" apiserveroptions "k8s.io/apiserver/pkg/server/options" "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -47,7 +48,6 @@ import ( logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" "k8s.io/klog/v2" schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" "k8s.io/kubernetes/pkg/scheduler" @@ -63,7 +63,7 @@ type Options struct { // The default values. ComponentConfig *kubeschedulerconfig.KubeSchedulerConfiguration - SecureServing *apiserveroptions.SecureServingOptionsWithLoopback + SecureServing *apiserveroptions.SecureServingOptions Authentication *apiserveroptions.DelegatingAuthenticationOptions Authorization *apiserveroptions.DelegatingAuthorizationOptions Metrics *metrics.Options @@ -103,7 +103,7 @@ func NewOptions() *Options { func NewOptionsWithComponentGlobalsRegistry(componentGlobalsRegistry basecompatibility.ComponentGlobalsRegistry) *Options { o := &Options{ - SecureServing: apiserveroptions.NewSecureServingOptions().WithLoopback(), + SecureServing: apiserveroptions.NewSecureServingOptions(), Authentication: apiserveroptions.NewDelegatingAuthenticationOptions(), Authorization: apiserveroptions.NewDelegatingAuthorizationOptions(), Deprecated: &DeprecatedOptions{ @@ -260,7 +260,7 @@ func (o *Options) ApplyTo(logger klog.Logger, c *schedulerappconfig.Config) erro } c.KubeConfig = kubeConfig - if err := o.SecureServing.ApplyTo(&c.SecureServing, &c.LoopbackClientConfig); err != nil { + if err := o.SecureServing.ApplyTo(&c.SecureServing); err != nil { return err } if o.SecureServing != nil && (o.SecureServing.BindPort != 0 || o.SecureServing.Listener != nil) { diff --git a/cmd/kube-scheduler/app/options/options_test.go b/cmd/kube-scheduler/app/options/options_test.go index 518daa0736b91..2e35f373f3271 100644 --- a/cmd/kube-scheduler/app/options/options_test.go +++ b/cmd/kube-scheduler/app/options/options_test.go @@ -306,7 +306,7 @@ profiles: PairName: "kube-scheduler", }, HTTP2MaxStreamsPerConnection: 47, - }).WithLoopback(), + }), Authentication: &apiserveroptions.DelegatingAuthenticationOptions{ CacheTTL: 10 * time.Second, ClientCert: apiserveroptions.ClientCertAuthenticationOptions{}, @@ -413,7 +413,7 @@ profiles: PairName: "kube-scheduler", }, HTTP2MaxStreamsPerConnection: 47, - }).WithLoopback(), + }), Authentication: &apiserveroptions.DelegatingAuthenticationOptions{ CacheTTL: 10 * time.Second, ClientCert: apiserveroptions.ClientCertAuthenticationOptions{}, @@ -487,7 +487,7 @@ profiles: PairName: "kube-scheduler", }, HTTP2MaxStreamsPerConnection: 47, - }).WithLoopback(), + }), Authentication: &apiserveroptions.DelegatingAuthenticationOptions{ CacheTTL: 10 * time.Second, RequestHeader: apiserveroptions.RequestHeaderAuthenticationOptions{ diff --git a/cmd/kube-scheduler/app/server.go b/cmd/kube-scheduler/app/server.go index 9a8eb04dacd01..0468a6921cd69 100644 --- a/cmd/kube-scheduler/app/server.go +++ b/cmd/kube-scheduler/app/server.go @@ -37,9 +37,11 @@ import ( apirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/server" genericfilters "k8s.io/apiserver/pkg/server/filters" + "k8s.io/apiserver/pkg/server/flagz" "k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/routes" + "k8s.io/apiserver/pkg/server/statusz" "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" @@ -60,8 +62,6 @@ import ( utilversion "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" - "k8s.io/component-base/zpages/statusz" "k8s.io/klog/v2" schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" "k8s.io/kubernetes/cmd/kube-scheduler/app/options" @@ -402,7 +402,7 @@ func newEndpointsHandler(config *kubeschedulerconfig.KubeSchedulerConfiguration, } if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentStatusz) { - statusz.Install(pathRecorderMux, kubeScheduler, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion())) + statusz.Install(pathRecorderMux, kubeScheduler, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion(), statusz.WithListedPaths(pathRecorderMux.ListedPaths()))) } return pathRecorderMux diff --git a/cmd/kube-scheduler/app/server_test.go b/cmd/kube-scheduler/app/server_test.go index 1d012a8c36a04..6e418cb8c1bf1 100644 --- a/cmd/kube-scheduler/app/server_test.go +++ b/cmd/kube-scheduler/app/server_test.go @@ -44,7 +44,6 @@ import ( "k8s.io/kubernetes/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/pkg/scheduler/apis/config" "k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults" - "k8s.io/kubernetes/pkg/scheduler/framework" ) func TestSetup(t *testing.T) { @@ -272,6 +271,7 @@ leaderElection: plugins.Score.Enabled = []config.Plugin{ {Name: "InterPodAffinity", Weight: 1}, {Name: "TaintToleration", Weight: 1}, + {Name: "DynamicResources", Weight: 1}, } return plugins }(), @@ -510,22 +510,22 @@ leaderElection: // Simulates an out-of-tree plugin. type foo struct{} -var _ framework.PreFilterPlugin = &foo{} -var _ framework.FilterPlugin = &foo{} +var _ fwk.PreFilterPlugin = &foo{} +var _ fwk.FilterPlugin = &foo{} func (*foo) Name() string { return "Foo" } -func newFoo(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) { +func newFoo(_ context.Context, _ runtime.Object, _ fwk.Handle) (fwk.Plugin, error) { return &foo{}, nil } -func (*foo) PreFilter(_ context.Context, _ fwk.CycleState, _ *v1.Pod, _ []fwk.NodeInfo) (*framework.PreFilterResult, *fwk.Status) { +func (*foo) PreFilter(_ context.Context, _ fwk.CycleState, _ *v1.Pod, _ []fwk.NodeInfo) (*fwk.PreFilterResult, *fwk.Status) { return nil, nil } -func (*foo) PreFilterExtensions() framework.PreFilterExtensions { +func (*foo) PreFilterExtensions() fwk.PreFilterExtensions { return nil } diff --git a/cmd/kube-scheduler/app/testing/testserver.go b/cmd/kube-scheduler/app/testing/testserver.go index 28ce7bad9de27..f76bd4badf3e2 100644 --- a/cmd/kube-scheduler/app/testing/testserver.go +++ b/cmd/kube-scheduler/app/testing/testserver.go @@ -20,7 +20,7 @@ import ( "context" "fmt" "net" - "os" + "testing" "time" "github.com/spf13/pflag" @@ -29,9 +29,10 @@ import ( utilcompatibility "k8s.io/apiserver/pkg/util/compatibility" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes" - restclient "k8s.io/client-go/rest" "k8s.io/component-base/compatibility" "k8s.io/component-base/configz" + "k8s.io/component-base/featuregate" + featuregatetesting "k8s.io/component-base/featuregate/testing" logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" "k8s.io/kubernetes/cmd/kube-scheduler/app" @@ -51,11 +52,10 @@ type TearDownFunc func() // TestServer return values supplied by kube-test-ApiServer type TestServer struct { - LoopbackClientConfig *restclient.Config // Rest client config using the magic token - Options *options.Options - Config *kubeschedulerconfig.Config - TearDownFn TearDownFunc // TearDown function - TmpDir string // Temp Dir used, by the apiserver + Options *options.Options + Config *kubeschedulerconfig.Config + TearDownFn TearDownFunc // TearDown function + TmpDir string // Temp Dir used, by the apiserver } // StartTestServer starts a kube-scheduler. A rest client config and a tear-down func, @@ -65,7 +65,7 @@ type TestServer struct { // // files that because Golang testing's call to os.Exit will not give a stop channel go routine // enough time to remove temporary files. -func StartTestServer(ctx context.Context, customFlags []string) (result TestServer, err error) { +func StartTestServer(t *testing.T, ctx context.Context, customFlags []string) (result TestServer, err error) { logger := klog.FromContext(ctx) ctx, cancel := context.WithCancel(ctx) @@ -81,9 +81,6 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ logger.Error(err, "Failed to shutdown test server clearly") } } - if len(result.TmpDir) != 0 { - os.RemoveAll(result.TmpDir) - } configz.Delete("componentconfig") } defer func() { @@ -92,10 +89,7 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } }() - result.TmpDir, err = os.MkdirTemp("", "kube-scheduler") - if err != nil { - return result, fmt.Errorf("failed to create temp dir: %v", err) - } + result.TmpDir = t.TempDir() fs := pflag.NewFlagSet("test", pflag.PanicOnError) @@ -104,6 +98,7 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ featureGate := utilfeature.DefaultMutableFeatureGate.DeepCopy() effectiveVersion := utilcompatibility.DefaultKubeEffectiveVersionForTest() effectiveVersion.SetEmulationVersion(featureGate.EmulationVersion()) + effectiveVersion.SetMinCompatibilityVersion(featureGate.MinCompatibilityVersion()) componentGlobalsRegistry := compatibility.NewComponentGlobalsRegistry() if err := componentGlobalsRegistry.Register(compatibility.DefaultKubeComponent, effectiveVersion, featureGate); err != nil { return result, err @@ -120,18 +115,18 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } // If the local ComponentGlobalsRegistry is changed by the flags, // we need to copy the new feature values back to the DefaultFeatureGate because most feature checks still use the DefaultFeatureGate. - if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) { - if err := utilfeature.DefaultMutableFeatureGate.SetEmulationVersion(effectiveVersion.EmulationVersion()); err != nil { - return result, err - } + if !featureGate.EmulationVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.EmulationVersion()) || !featureGate.MinCompatibilityVersion().EqualTo(utilfeature.DefaultMutableFeatureGate.MinCompatibilityVersion()) { + featuregatetesting.SetFeatureGateVersionsDuringTest(t, utilfeature.DefaultMutableFeatureGate, effectiveVersion.EmulationVersion(), effectiveVersion.MinCompatibilityVersion()) } + featureOverrides := map[featuregate.Feature]bool{} for f := range utilfeature.DefaultMutableFeatureGate.GetAll() { if featureGate.Enabled(f) != utilfeature.DefaultFeatureGate.Enabled(f) { - if err := utilfeature.DefaultMutableFeatureGate.Set(fmt.Sprintf("%s=%v", f, featureGate.Enabled(f))); err != nil { - return result, err - } + featureOverrides[f] = featureGate.Enabled(f) } } + if len(featureOverrides) > 0 { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featureOverrides) + } if opts.SecureServing.BindPort != 0 { opts.SecureServing.Listener, opts.SecureServing.BindPort, err = createListenerOnFreePort() @@ -143,6 +138,9 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ logger.Info("kube-scheduler will listen securely", "port", opts.SecureServing.BindPort) } + // Always exempt /healthz from authorization because we use it to verify startup below. + opts.Authorization.AlwaysAllowPaths = append(opts.Authorization.AlwaysAllowPaths, "/healthz") + cc, sched, err := app.Setup(ctx, opts) if err != nil { return result, fmt.Errorf("failed to create config from options: %v", err) @@ -157,10 +155,22 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ }(ctx) logger.Info("Waiting for /healthz to be ok...") - client, err := kubernetes.NewForConfig(cc.LoopbackClientConfig) + + // Cannot use cc.Kubeconfig since it is overwritten to point to kube-apiserver, + // build our own client instead. + serverCert, _ := cc.SecureServing.Cert.CurrentCertKeyContent() + clientConfig, err := cc.SecureServing.NewClientConfig(serverCert) if err != nil { - return result, fmt.Errorf("failed to create a client: %v", err) + return result, fmt.Errorf("getting secure serving rest config %w", err) } + + // The server cert only includes IPv4 localhost, not IPv6. + clientConfig.TLSClientConfig.ServerName = "127.0.0.1" + client, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + return result, fmt.Errorf("creating client: %w", err) + } + err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 30*time.Second, false, func(ctx context.Context) (bool, error) { select { case err := <-errCh: @@ -168,12 +178,12 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ default: } - result := client.CoreV1().RESTClient().Get().AbsPath("/healthz").Do(ctx) status := 0 - result.StatusCode(&status) + client.CoreV1().RESTClient().Get().AbsPath("/healthz").Do(ctx).StatusCode(&status) if status == 200 { return true, nil } + return false, nil }) if err != nil { @@ -181,7 +191,6 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } // from here the caller must call tearDown - result.LoopbackClientConfig = cc.LoopbackClientConfig result.Options = opts result.Config = cc.Config result.TearDownFn = tearDown @@ -190,13 +199,12 @@ func StartTestServer(ctx context.Context, customFlags []string) (result TestServ } // StartTestServerOrDie calls StartTestServer panic if it does not succeed. -func StartTestServerOrDie(ctx context.Context, flags []string) *TestServer { - result, err := StartTestServer(ctx, flags) - if err == nil { - return &result +func StartTestServerOrDie(t *testing.T, ctx context.Context, flags []string) *TestServer { + result, err := StartTestServer(t, ctx, flags) + if err != nil { + t.Fatalf("failed to launch server: %v", err) } - - panic(fmt.Errorf("failed to launch server: %v", err)) + return &result } func createListenerOnFreePort() (net.Listener, int, error) { diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index e7616542eafe2..3d311930e767f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -37,6 +37,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { fuzzDNS, fuzzNodeRegistration, fuzzLocalEtcd, + fuzzExternalEtcd, fuzzNetworking, fuzzJoinConfiguration, fuzzJoinControlPlane, @@ -116,6 +117,15 @@ func fuzzLocalEtcd(obj *kubeadm.LocalEtcd, c randfill.Continue) { obj.DataDir = "foo" } +// TODO: Remove this once v1beta3 API was removed. +func fuzzExternalEtcd(obj *kubeadm.ExternalEtcd, c randfill.Continue) { + c.FillNoCustom(obj) + + // Ensure HTTPEndpoints equals Endpoints to maintain roundtrip compatibility + // with v1beta3 which doesn't have HTTPEndpoints field + obj.HTTPEndpoints = obj.Endpoints +} + func fuzzNetworking(obj *kubeadm.Networking, c randfill.Continue) { c.FillNoCustom(obj) diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index ee693873f7dfd..4b7acc2fe325c 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -140,10 +140,12 @@ type ClusterConfiguration struct { // +k8s:conversion-gen=false CIImageRepository string - // FeatureGates enabled by the user. + // FeatureGates holds kubeadm-specific feature gates enabled by the user. FeatureGates map[string]bool - // The cluster name + // The cluster name. This name will be used in kubeconfig files generated by kubeadm + // and will also be passed as a value to the kube-controller-manager's --cluster-name flag. + // Default value is 'kubernetes'. ClusterName string // EncryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. @@ -165,6 +167,7 @@ type ControlPlaneComponent struct { // An argument name in this list is the flag name as it appears on the // command line except without leading dash(es). Extra arguments will override existing // default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. ExtraArgs []Arg // ExtraVolumes is an extra set of host volumes, mounted to the control plane component. @@ -247,6 +250,7 @@ type NodeRegistrationOptions struct { // Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. // An argument name in this list is the flag name as it appears on the command line except without leading dash(es). // Extra arguments will override existing default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. KubeletExtraArgs []Arg // IgnorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered, e.g. 'IsPrivilegedUser,Swap'. @@ -286,7 +290,8 @@ type Etcd struct { // LocalEtcd describes that kubeadm should run an etcd cluster locally type LocalEtcd struct { - // ImageMeta allows to customize the container used for etcd + // ImageMeta allows to customize the container image used for etcd. Passing a custom etcd image + // tells kubeadm upgrade that this image is user-managed and that its upgrade must be skipped. ImageMeta `json:",inline"` // DataDir is the directory etcd will place its data. @@ -298,6 +303,7 @@ type LocalEtcd struct { // An argument name in this list is the flag name as it appears on the // command line except without leading dash(es). Extra arguments will override existing // default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. ExtraArgs []Arg // ExtraEnvs is an extra set of environment variables to pass to the control plane component. @@ -313,10 +319,23 @@ type LocalEtcd struct { // ExternalEtcd describes an external etcd cluster type ExternalEtcd struct { - - // Endpoints of etcd members. Useful for using external etcd. - // If not provided, kubeadm will run etcd in a static pod. + // Endpoints of etcd members. Required when using external etcd. + // Specifies the client URLs (usually gRPC endpoints) for etcd communication. + // By default, these endpoints handle both gRPC traffic (primary etcd protocol) + // and HTTP traffic (metrics, health checks). However, if HTTPEndpoints is configured, + // the gRPC and HTTP traffic can be separated for better security and performance. + // Corresponds to etcd's --listen-client-urls configuration. Endpoints []string + + // HTTPEndpoints are the dedicated HTTP endpoints for etcd communication. + // When configured, HTTP traffic (such as /metrics and /health endpoints) is separated + // from the gRPC traffic handled by Endpoints. This separation allows for better access + // control, as HTTP endpoints can be exposed without exposing the primary gRPC interface. + // Corresponds to etcd's --listen-client-http-urls configuration. + // If not provided, Endpoints will be used for both gRPC and HTTP traffic. + // +optional + HTTPEndpoints []string + // CAFile is an SSL Certificate Authority file used to secure etcd communication. CAFile string // CertFile is an SSL certification file used to secure etcd communication. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go index ee882cf669da8..5d9e6b6c0adba 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go @@ -161,3 +161,17 @@ func convertFromArgs(in []kubeadm.Arg) map[string]string { func Convert_v1beta3_APIServer_To_kubeadm_APIServer(in *APIServer, out *kubeadm.APIServer, s conversion.Scope) error { return autoConvert_v1beta3_APIServer_To_kubeadm_APIServer(in, out, s) } + +// Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd converts a private ExternalEtcd to public ExternalEtcd. +func Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(in *kubeadm.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { + return autoConvert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(in, out, s) +} + +// Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd converts a public ExternalEtcd to private ExternalEtcd. +// It is required due to missing HTTPEndpoints in v1beta3. +func Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(in *ExternalEtcd, out *kubeadm.ExternalEtcd, s conversion.Scope) error { + // set the HTTPEndpoints to the same as the Endpoints + // this is to maintain backwards compatibility with the v1beta3 API + out.HTTPEndpoints = in.Endpoints + return autoConvert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(in, out, s) +} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go index f46ce0fdaef89..74aa4d79744d4 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/types.go @@ -129,11 +129,13 @@ type ClusterConfiguration struct { // +optional ImageRepository string `json:"imageRepository,omitempty"` - // FeatureGates enabled by the user. + // FeatureGates holds kubeadm-specific feature gates enabled by the user. // +optional FeatureGates map[string]bool `json:"featureGates,omitempty"` - // The cluster name + // The cluster name. This name will be used in kubeconfig files generated by kubeadm + // and will also be passed as a value to the kube-controller-manager's --cluster-name flag. + // Default value is 'kubernetes'. // +optional ClusterName string `json:"clusterName,omitempty"` } @@ -270,7 +272,8 @@ type Etcd struct { // LocalEtcd describes that kubeadm should run an etcd cluster locally type LocalEtcd struct { - // ImageMeta allows to customize the container used for etcd + // ImageMeta allows to customize the container image used for etcd. Passing a custom etcd image + // tells kubeadm upgrade that this image is user-managed and that its upgrade must be skipped. ImageMeta `json:",inline"` // DataDir is the directory etcd will place its data. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go index 3e8c3f69eb301..609d7d06389e8 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go @@ -89,16 +89,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ExternalEtcd)(nil), (*kubeadm.ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(a.(*ExternalEtcd), b.(*kubeadm.ExternalEtcd), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*kubeadm.ExternalEtcd)(nil), (*ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(a.(*kubeadm.ExternalEtcd), b.(*ExternalEtcd), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*FileDiscovery)(nil), (*kubeadm.FileDiscovery)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta3_FileDiscovery_To_kubeadm_FileDiscovery(a.(*FileDiscovery), b.(*kubeadm.FileDiscovery), scope) }); err != nil { @@ -174,6 +164,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*kubeadm.ExternalEtcd)(nil), (*ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(a.(*kubeadm.ExternalEtcd), b.(*ExternalEtcd), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*kubeadm.InitConfiguration)(nil), (*InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_kubeadm_InitConfiguration_To_v1beta3_InitConfiguration(a.(*kubeadm.InitConfiguration), b.(*InitConfiguration), scope) }); err != nil { @@ -209,6 +204,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ExternalEtcd)(nil), (*kubeadm.ExternalEtcd)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(a.(*ExternalEtcd), b.(*kubeadm.ExternalEtcd), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*InitConfiguration)(nil), (*kubeadm.InitConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta3_InitConfiguration_To_kubeadm_InitConfiguration(a.(*InitConfiguration), b.(*kubeadm.InitConfiguration), scope) }); err != nil { @@ -435,7 +435,15 @@ func autoConvert_v1beta3_Etcd_To_kubeadm_Etcd(in *Etcd, out *kubeadm.Etcd, s con } else { out.Local = nil } - out.External = (*kubeadm.ExternalEtcd)(unsafe.Pointer(in.External)) + if in.External != nil { + in, out := &in.External, &out.External + *out = new(kubeadm.ExternalEtcd) + if err := Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.External = nil + } return nil } @@ -454,7 +462,15 @@ func autoConvert_kubeadm_Etcd_To_v1beta3_Etcd(in *kubeadm.Etcd, out *Etcd, s con } else { out.Local = nil } - out.External = (*ExternalEtcd)(unsafe.Pointer(in.External)) + if in.External != nil { + in, out := &in.External, &out.External + *out = new(ExternalEtcd) + if err := Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(*in, *out, s); err != nil { + return err + } + } else { + out.External = nil + } return nil } @@ -471,24 +487,15 @@ func autoConvert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(in *ExternalEtcd, return nil } -// Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd is an autogenerated conversion function. -func Convert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(in *ExternalEtcd, out *kubeadm.ExternalEtcd, s conversion.Scope) error { - return autoConvert_v1beta3_ExternalEtcd_To_kubeadm_ExternalEtcd(in, out, s) -} - func autoConvert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(in *kubeadm.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + // WARNING: in.HTTPEndpoints requires manual conversion: does not exist in peer-type out.CAFile = in.CAFile out.CertFile = in.CertFile out.KeyFile = in.KeyFile return nil } -// Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd is an autogenerated conversion function. -func Convert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(in *kubeadm.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { - return autoConvert_kubeadm_ExternalEtcd_To_v1beta3_ExternalEtcd(in, out, s) -} - func autoConvert_v1beta3_FileDiscovery_To_kubeadm_FileDiscovery(in *FileDiscovery, out *kubeadm.FileDiscovery, s conversion.Scope) error { out.KubeConfigPath = in.KubeConfigPath return nil diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go index 4f031ca6aabdf..bbf449092dd28 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go @@ -130,6 +130,19 @@ func SetDefaults_Etcd(obj *ClusterConfiguration) { obj.Etcd.Local.DataDir = DefaultEtcdDataDir } } + if obj.Etcd.External != nil { + SetDefaults_ExternalEtcd(obj.Etcd.External) + } +} + +// SetDefaults_ExternalEtcd assigns default values for the external etcd +func SetDefaults_ExternalEtcd(obj *ExternalEtcd) { + // If HTTPEndpoints is not set, default it to Endpoints + // This allows HTTP traffic (metrics, health checks) to use the same endpoints as gRPC traffic + if len(obj.HTTPEndpoints) == 0 && len(obj.Endpoints) > 0 { + obj.HTTPEndpoints = make([]string, len(obj.Endpoints)) + copy(obj.HTTPEndpoints, obj.Endpoints) + } } // SetDefaults_JoinConfiguration assigns default values to a regular node diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go index 9f3e88e965f7c..70c5fba35564d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go @@ -23,6 +23,12 @@ limitations under the License. // This version improves on the v1beta3 format by fixing some minor issues and adding a few new fields. // // A list of changes since v1beta3: +// v1.35: +// - Add `HTTPEndpoints` field to `ClusterConfiguration.Etcd.ExternalEtcd` that can be used to configure the HTTP endpoints for etcd communication in v1beta4. +// This field is used to separate the HTTP traffic (such as /metrics and /health endpoints) from the gRPC traffic handled by Endpoints. +// This separation allows for better access control, as HTTP endpoints can be exposed without exposing the primary gRPC interface. +// Corresponds to etcd's --listen-client-http-urls configuration. +// If not provided, Endpoints will be used for both gRPC and HTTP traffic. // // v1.34: // - Add "ECDSA-P384" to the allowed encryption algorithm options for `ClusterConfiguration.EncryptionAlgorithm`. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go index 9bac88d1d9ee0..7da4c05063e81 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go @@ -140,11 +140,13 @@ type ClusterConfiguration struct { // +optional ImageRepository string `json:"imageRepository,omitempty"` - // FeatureGates enabled by the user. + // FeatureGates holds kubeadm-specific feature gates enabled by the user. // +optional FeatureGates map[string]bool `json:"featureGates,omitempty"` - // The cluster name + // The cluster name. This name will be used in kubeconfig files generated by kubeadm + // and will also be passed as a value to the kube-controller-manager's --cluster-name flag. + // Default value is 'kubernetes'. // +optional ClusterName string `json:"clusterName,omitempty"` @@ -170,6 +172,7 @@ type ControlPlaneComponent struct { // An argument name in this list is the flag name as it appears on the // command line except without leading dash(es). Extra arguments will override existing // default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. // +optional ExtraArgs []Arg `json:"extraArgs,omitempty"` @@ -260,6 +263,7 @@ type NodeRegistrationOptions struct { // Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on. // An argument name in this list is the flag name as it appears on the command line except without leading dash(es). // Extra arguments will override existing default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. // +optional KubeletExtraArgs []Arg `json:"kubeletExtraArgs,omitempty"` @@ -309,7 +313,8 @@ type Etcd struct { // LocalEtcd describes that kubeadm should run an etcd cluster locally type LocalEtcd struct { - // ImageMeta allows to customize the container used for etcd + // ImageMeta allows to customize the container image used for etcd. Passing a custom etcd image + // tells kubeadm upgrade that this image is user-managed and that its upgrade must be skipped. ImageMeta `json:",inline"` // DataDir is the directory etcd will place its data. @@ -321,6 +326,7 @@ type LocalEtcd struct { // An argument name in this list is the flag name as it appears on the // command line except without leading dash(es). Extra arguments will override existing // default arguments. Duplicate extra arguments are allowed. + // The default arguments are sorted alpha-numerically but the extra arguments are not. // +optional ExtraArgs []Arg `json:"extraArgs,omitempty"` @@ -340,9 +346,23 @@ type LocalEtcd struct { // ExternalEtcd describes an external etcd cluster. // Kubeadm has no knowledge of where certificate files live and they must be supplied. type ExternalEtcd struct { - // Endpoints of etcd members. Required for ExternalEtcd. + // Endpoints of etcd members. Required when using external etcd. + // Specifies the client URLs (usually gRPC endpoints) for etcd communication. + // By default, these endpoints handle both gRPC traffic (primary etcd protocol) + // and HTTP traffic (metrics, health checks). However, if HTTPEndpoints is configured, + // the gRPC and HTTP traffic can be separated for better security and performance. + // Corresponds to etcd's --listen-client-urls configuration. Endpoints []string `json:"endpoints"` + // HTTPEndpoints are the dedicated HTTP endpoints for etcd communication. + // When configured, HTTP traffic (such as /metrics and /health endpoints) is separated + // from the gRPC traffic handled by Endpoints. This separation allows for better access + // control, as HTTP endpoints can be exposed without exposing the primary gRPC interface. + // Corresponds to etcd's --listen-client-http-urls configuration. + // If not provided, Endpoints will be used for both gRPC and HTTP traffic. + // +optional + HTTPEndpoints []string `json:"httpEndpoints,omitempty"` + // CAFile is an SSL Certificate Authority file used to secure etcd communication. // Required if using a TLS connection. CAFile string `json:"caFile"` diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go index f781a06dd34de..c502378adcfee 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go @@ -609,6 +609,7 @@ func Convert_kubeadm_Etcd_To_v1beta4_Etcd(in *kubeadm.Etcd, out *Etcd, s convers func autoConvert_v1beta4_ExternalEtcd_To_kubeadm_ExternalEtcd(in *ExternalEtcd, out *kubeadm.ExternalEtcd, s conversion.Scope) error { out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.HTTPEndpoints = *(*[]string)(unsafe.Pointer(&in.HTTPEndpoints)) out.CAFile = in.CAFile out.CertFile = in.CertFile out.KeyFile = in.KeyFile @@ -622,6 +623,7 @@ func Convert_v1beta4_ExternalEtcd_To_kubeadm_ExternalEtcd(in *ExternalEtcd, out func autoConvert_kubeadm_ExternalEtcd_To_v1beta4_ExternalEtcd(in *kubeadm.ExternalEtcd, out *ExternalEtcd, s conversion.Scope) error { out.Endpoints = *(*[]string)(unsafe.Pointer(&in.Endpoints)) + out.HTTPEndpoints = *(*[]string)(unsafe.Pointer(&in.HTTPEndpoints)) out.CAFile = in.CAFile out.CertFile = in.CertFile out.KeyFile = in.KeyFile diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go index b888f2342f8bc..607e8973fa69b 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go @@ -279,6 +279,11 @@ func (in *ExternalEtcd) DeepCopyInto(out *ExternalEtcd) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.HTTPEndpoints != nil { + in, out := &in.HTTPEndpoints, &out.HTTPEndpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go index 8c3834d9649d1..d5a0dac530493 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.defaults.go @@ -45,6 +45,9 @@ func SetObjectDefaults_ClusterConfiguration(in *ClusterConfiguration) { SetDefaults_EnvVar(a) } } + if in.Etcd.External != nil { + SetDefaults_ExternalEtcd(in.Etcd.External) + } for i := range in.APIServer.ControlPlaneComponent.ExtraEnvs { a := &in.APIServer.ControlPlaneComponent.ExtraEnvs[i] SetDefaults_EnvVar(a) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index ec3cb843e5af4..967988aa2ca70 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -329,7 +329,14 @@ func ValidateEtcd(e *kubeadm.Etcd, fldPath *field.Path) field.ErrorList { allErrs = append(allErrs, field.Invalid(externalPath, "", "setting .Etcd.External.CertFile and .Etcd.External.KeyFile requires .Etcd.External.CAFile")) } - allErrs = append(allErrs, ValidateURLs(e.External.Endpoints, requireHTTPS, externalPath.Child("endpoints"))...) + if len(e.External.Endpoints) == 0 { + allErrs = append(allErrs, field.Invalid(externalPath.Child("endpoints"), "", "at least one endpoint must be specified")) + } else { + allErrs = append(allErrs, ValidateURLs(e.External.Endpoints, requireHTTPS, externalPath.Child("endpoints"))...) + } + + allErrs = append(allErrs, ValidateURLs(e.External.HTTPEndpoints, false /* requireHTTPS */, externalPath.Child("httpEndpoints"))...) + if e.External.CAFile != "" { allErrs = append(allErrs, ValidateAbsolutePath(e.External.CAFile, externalPath.Child("caFile"))...) } diff --git a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go index 188d34c7cdbd9..04761aa9f3811 100644 --- a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go @@ -318,6 +318,11 @@ func (in *ExternalEtcd) DeepCopyInto(out *ExternalEtcd) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.HTTPEndpoints != nil { + in, out := &in.HTTPEndpoints, &out.HTTPEndpoints + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/cmd/kubeadm/app/cmd/config.go b/cmd/kubeadm/app/cmd/config.go index 3907b0651f0ec..be8d486b531a6 100644 --- a/cmd/kubeadm/app/cmd/config.go +++ b/cmd/kubeadm/app/cmd/config.go @@ -387,6 +387,7 @@ func newCmdConfigImagesPull() *cobra.Command { if err := containerRuntime.Connect(); err != nil { return err } + defer containerRuntime.Close() return PullControlPlaneImages(containerRuntime, &internalcfg.ClusterConfiguration) }, Args: cobra.NoArgs, diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index e25b9dea49fa8..a52b2952cf0ed 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -353,11 +353,11 @@ func newInitData(cmd *cobra.Command, args []string, initOptions *initOptions, ou return nil, err } - // if dry running creates a temporary folder for saving kubeadm generated files + // If dry running creates a temporary directory for saving kubeadm generated files. dryRunDir := "" if initOptions.dryRun || cfg.DryRun { if dryRunDir, err = kubeadmconstants.GetDryRunDir(kubeadmconstants.EnvVarInitDryRunDir, "kubeadm-init-dryrun", klog.Warningf); err != nil { - return nil, errors.Wrap(err, "couldn't create a temporary directory") + return nil, errors.Wrap(err, "could not create a temporary directory on dryrun") } } @@ -477,7 +477,7 @@ func (d *initData) KubeConfig() (*clientcmdapi.Config, error) { return d.kubeconfig, nil } -// KubeConfigDir returns the path of the Kubernetes configuration folder or the temporary folder path in case of DryRun. +// KubeConfigDir returns the Kubernetes configuration directory or the temporary directory if DryRun is true. func (d *initData) KubeConfigDir() string { if d.dryRun { return d.dryRunDir @@ -493,7 +493,7 @@ func (d *initData) KubeConfigPath() string { return d.kubeconfigPath } -// ManifestDir returns the path where manifest should be stored or the temporary folder path in case of DryRun. +// ManifestDir returns the path where manifest should be stored or the temporary directory if DryRun is true. func (d *initData) ManifestDir() string { if d.dryRun { return d.dryRunDir @@ -501,7 +501,7 @@ func (d *initData) ManifestDir() string { return kubeadmconstants.GetStaticPodDirectory() } -// KubeletDir returns path of the kubelet configuration folder or the temporary folder in case of DryRun. +// KubeletDir returns the kubelet configuration directory or the temporary directory if DryRun is true. func (d *initData) KubeletDir() string { if d.dryRun { return d.dryRunDir diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 12fce4a1cc5ca..20920abe6e12d 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -465,11 +465,11 @@ func newJoinData(cmd *cobra.Command, args []string, opt *joinOptions, out io.Wri } } - // if dry running, creates a temporary folder to save kubeadm generated files + // If dry running creates a temporary directory for saving kubeadm generated files. dryRunDir := "" if opt.dryRun || cfg.DryRun { if dryRunDir, err = kubeadmconstants.GetDryRunDir(kubeadmconstants.EnvVarJoinDryRunDir, "kubeadm-join-dryrun", klog.Warningf); err != nil { - return nil, errors.Wrap(err, "couldn't create a temporary directory on dryrun") + return nil, errors.Wrap(err, "could not create a temporary directory on dryrun") } } @@ -502,7 +502,7 @@ func (j *joinData) DryRun() bool { return j.dryRun } -// KubeConfigDir returns the path of the Kubernetes configuration folder or the temporary folder path in case of DryRun. +// KubeConfigDir returns the Kubernetes configuration directory or the temporary directory if DryRun is true. func (j *joinData) KubeConfigDir() string { if j.dryRun { return j.dryRunDir @@ -510,7 +510,7 @@ func (j *joinData) KubeConfigDir() string { return kubeadmconstants.KubernetesDir } -// KubeletDir returns the path of the kubelet configuration folder or the temporary folder in case of DryRun. +// KubeletDir returns the kubelet configuration directory or the temporary directory if DryRun is true. func (j *joinData) KubeletDir() string { if j.dryRun { return j.dryRunDir @@ -518,7 +518,7 @@ func (j *joinData) KubeletDir() string { return kubeadmconstants.KubeletRunDirectory } -// ManifestDir returns the path where manifest should be stored or the temporary folder path in case of DryRun. +// ManifestDir returns the path where manifest should be stored or the temporary directory if DryRun is true. func (j *joinData) ManifestDir() string { if j.dryRun { return j.dryRunDir @@ -526,7 +526,7 @@ func (j *joinData) ManifestDir() string { return kubeadmconstants.GetStaticPodDirectory() } -// CertificateWriteDir returns the path where certs should be stored or the temporary folder path in case of DryRun. +// CertificateWriteDir returns the path where certs should be stored or the temporary directory if DryRun is true. func (j *joinData) CertificateWriteDir() string { if j.dryRun { return j.dryRunDir diff --git a/cmd/kubeadm/app/cmd/phases/init/kubeconfig.go b/cmd/kubeadm/app/cmd/phases/init/kubeconfig.go index 2e95cd1990bc6..d8ce56ad360f2 100644 --- a/cmd/kubeadm/app/cmd/phases/init/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/phases/init/kubeconfig.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" - "k8s.io/kubernetes/cmd/kubeadm/app/features" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" @@ -158,11 +157,9 @@ func runKubeConfigFile(kubeConfigFileName string) func(workflow.RunData) error { initConfiguration := data.Cfg().DeepCopy() - if features.Enabled(cfg.FeatureGates, features.ControlPlaneKubeletLocalMode) { - if kubeConfigFileName == kubeadmconstants.KubeletKubeConfigFileName { - // Unset the ControlPlaneEndpoint so the creation falls back to the LocalAPIEndpoint for the kubelet's kubeconfig. - initConfiguration.ControlPlaneEndpoint = "" - } + if kubeConfigFileName == kubeadmconstants.KubeletKubeConfigFileName { + // Unset the ControlPlaneEndpoint so the creation falls back to the LocalAPIEndpoint for the kubelet's kubeconfig. + initConfiguration.ControlPlaneEndpoint = "" } // creates the KubeConfig file (or use existing) diff --git a/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go b/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go index 860175e8adda1..c30df5f65779d 100644 --- a/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go +++ b/cmd/kubeadm/app/cmd/phases/join/controlplanejoin.go @@ -24,7 +24,6 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" - "k8s.io/kubernetes/cmd/kubeadm/app/features" etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd" markcontrolplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markcontrolplane" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" @@ -83,32 +82,26 @@ func NewControlPlaneJoinPhase() workflow.Phase { func NewEtcdJoinPhase() workflow.Phase { return workflow.Phase{ Name: "etcd-join", - Short: fmt.Sprintf("[EXPERIMENTAL] Join etcd for control plane nodes (only used when feature gate %s is enabled)", features.ControlPlaneKubeletLocalMode), + Short: "Join etcd for control plane nodes", Run: runEtcdPhase, 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) - }, } } +// TODO: Deprecated. Remove once ControlPlaneKubeletLocalMode is removed in 1.36. +// https://github.com/kubernetes/kubeadm/issues/2271 func newEtcdLocalSubphase() workflow.Phase { return workflow.Phase{ Name: "etcd", - Short: "Add a new local etcd member", + Short: "[DEPRECATED] Add a new local etcd member. Deprecated in favor of 'etcd-join' and will be removed in 1.36", Run: runEtcdPhase, InheritFlags: getControlPlaneJoinPhaseFlags("etcd"), ArgsValidator: cobra.NoArgs, - // Only run this phase as subphase of control-plane-join phase if - // `ControlPlaneKubeletLocalMode` is deactivated. + Hidden: true, RunIf: func(c workflow.RunData) (bool, error) { - return checkFeatureState(c, features.ControlPlaneKubeletLocalMode, false) + return false, nil }, } } diff --git a/cmd/kubeadm/app/cmd/phases/join/data.go b/cmd/kubeadm/app/cmd/phases/join/data.go index f6b3e769b22e8..9e04d9032f9fe 100644 --- a/cmd/kubeadm/app/cmd/phases/join/data.go +++ b/cmd/kubeadm/app/cmd/phases/join/data.go @@ -25,9 +25,6 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" - "k8s.io/kubernetes/cmd/kubeadm/app/features" - "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" ) // JoinData is the interface to use for join phases. @@ -48,17 +45,3 @@ type JoinData interface { ManifestDir() string CertificateWriteDir() string } - -func checkFeatureState(c workflow.RunData, featureGate string, state bool) (bool, error) { - data, ok := c.(JoinData) - if !ok { - return false, errors.New("control-plane-join phase invoked with an invalid data struct") - } - - cfg, err := data.InitCfg() - if err != nil { - return false, err - } - - return state == features.Enabled(cfg.FeatureGates, featureGate), nil -} diff --git a/cmd/kubeadm/app/cmd/phases/join/kubelet.go b/cmd/kubeadm/app/cmd/phases/join/kubelet.go index 751ab305d5c4c..822bf7a509ff9 100644 --- a/cmd/kubeadm/app/cmd/phases/join/kubelet.go +++ b/cmd/kubeadm/app/cmd/phases/join/kubelet.go @@ -75,20 +75,13 @@ func NewKubeletStartPhase() workflow.Phase { func NewKubeletWaitBootstrapPhase() workflow.Phase { return workflow.Phase{ Name: "kubelet-wait-bootstrap", - Short: "[EXPERIMENTAL] Wait for the kubelet to bootstrap itself (only used when feature gate ControlPlaneKubeletLocalMode is enabled)", + Short: "Wait for the kubelet to bootstrap itself", Run: runKubeletWaitBootstrapPhase, InheritFlags: []string{ options.CfgPath, options.NodeCRISocket, options.DryRun, }, - // 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) - }, } } @@ -124,16 +117,6 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) { } bootstrapKubeConfigFile := filepath.Join(data.KubeConfigDir(), kubeadmconstants.KubeletBootstrapKubeConfigFileName) - // Do not delete the bootstrapKubeConfigFile at the end of this function when - // using ControlPlaneKubeletLocalMode. The KubeletWaitBootstrapPhase will delete - // it when the feature is enabled. - if !features.Enabled(initCfg.FeatureGates, features.ControlPlaneKubeletLocalMode) { - // Deletes the bootstrapKubeConfigFile, so the credential used for TLS bootstrap is removed from disk - defer func() { - _ = os.Remove(bootstrapKubeConfigFile) - }() - } - var client clientset.Interface // If dry-use the client from joinData, else create a new bootstrap client if data.DryRun() { @@ -148,17 +131,15 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) { } } - if features.Enabled(initCfg.FeatureGates, features.ControlPlaneKubeletLocalMode) { - // Set the server url to LocalAPIEndpoint if the feature gate is enabled so the config - // which gets passed to the kubelet forces it to talk to the local kube-apiserver. - if cfg.ControlPlane != nil { - for c, conf := range tlsBootstrapCfg.Clusters { - conf.Server, err = kubeadmutil.GetLocalAPIEndpoint(&cfg.ControlPlane.LocalAPIEndpoint) - if err != nil { - return errors.Wrapf(err, "could not get LocalAPIEndpoint when %s is enabled", features.ControlPlaneKubeletLocalMode) - } - tlsBootstrapCfg.Clusters[c] = conf + // Set the server url to LocalAPIEndpoint if the feature gate is enabled so the config + // which gets passed to the kubelet forces it to talk to the local kube-apiserver. + if cfg.ControlPlane != nil { + for c, conf := range tlsBootstrapCfg.Clusters { + conf.Server, err = kubeadmutil.GetLocalAPIEndpoint(&cfg.ControlPlane.LocalAPIEndpoint) + if err != nil { + return errors.Wrapf(err, "could not get LocalAPIEndpoint") } + tlsBootstrapCfg.Clusters[c] = conf } } @@ -252,13 +233,6 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) { fmt.Println("[kubelet-start] Starting the kubelet") kubeletphase.TryStartKubelet() - // Run the same code as KubeletWaitBootstrapPhase would do if the ControlPlaneKubeletLocalMode feature gate is disabled. - if !features.Enabled(initCfg.FeatureGates, features.ControlPlaneKubeletLocalMode) { - if err := runKubeletWaitBootstrapPhase(c); err != nil { - return err - } - } - return nil } diff --git a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go index 3a8a769edc48c..8a163a1686e55 100644 --- a/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go +++ b/cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go @@ -138,6 +138,8 @@ func removeContainers(criSocketPath string) error { if err := containerRuntime.Connect(); err != nil { return err } + defer containerRuntime.Close() + containers, err := containerRuntime.ListKubeContainers() if err != nil { return err diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/data_test.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/data_test.go index a5a55f1beb923..25ee332d16609 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/data_test.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/data_test.go @@ -45,3 +45,5 @@ func (t *testData) SessionIsInteractive() bool { return false } func (t *testData) AllowExperimentalUpgrades() bool { return false } func (t *testData) AllowRCUpgrades() bool { return false } func (t *testData) ForceUpgrade() bool { return false } +func (t *testData) KubeConfigDir() string { return "" } +func (t *testData) KubeletDir() string { return "" } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go b/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go index ba0d8adc50fbd..06d45cea242a0 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/apply/preflight.go @@ -70,7 +70,7 @@ func runPreflight(c workflow.RunData) error { if err := preflight.RunRootCheckOnly(ignorePreflightErrors); err != nil { return err } - if err := preflight.RunUpgradeChecks(ignorePreflightErrors); err != nil { + if err := preflight.RunUpgradeChecks(utilsexec.New(), initCfg, ignorePreflightErrors); err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/data.go b/cmd/kubeadm/app/cmd/phases/upgrade/data.go index 6b299d8aaa916..016199c90f67d 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/data.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/data.go @@ -38,4 +38,6 @@ type Data interface { IgnorePreflightErrors() sets.Set[string] PatchesDir() string OutputWriter() io.Writer + KubeConfigDir() string + KubeletDir() string } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/data_test.go b/cmd/kubeadm/app/cmd/phases/upgrade/data_test.go index bc2f1ce0d9332..1684b20679472 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/data_test.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/data_test.go @@ -41,3 +41,5 @@ func (t *testData) Client() clientset.Interface { return nil } func (t *testData) IgnorePreflightErrors() sets.Set[string] { return nil } func (t *testData) PatchesDir() string { return "" } func (t *testData) OutputWriter() io.Writer { return nil } +func (t *testData) KubeConfigDir() string { return "" } +func (t *testData) KubeletDir() string { return "" } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/kubeletconfig.go b/cmd/kubeadm/app/cmd/phases/upgrade/kubeletconfig.go index 6b98aef5c40bf..876f068d08634 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/kubeletconfig.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/kubeletconfig.go @@ -29,10 +29,6 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" ) -const ( - kubeletConfigDir = "" -) - var ( kubeletConfigLongDesc = cmdutil.LongDesc(` Upgrade the kubelet configuration for this node by downloading it from the kubelet-config ConfigMap stored in the cluster @@ -65,7 +61,7 @@ func runKubeletConfigPhase(c workflow.RunData) error { // Write the configuration for the kubelet down to disk and print the generated manifests instead of dry-running. // If not dry-running, the kubelet config file will be backed up to the /etc/kubernetes/tmp/ dir, so that it could be // recovered if anything goes wrong. - err := upgrade.WriteKubeletConfigFiles(data.InitCfg(), kubeletConfigDir, data.PatchesDir(), data.DryRun(), data.OutputWriter()) + err := upgrade.WriteKubeletConfigFiles(data.InitCfg(), data.KubeletDir(), data.KubeConfigDir(), data.PatchesDir(), data.DryRun(), data.OutputWriter()) if err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/node/data_test.go b/cmd/kubeadm/app/cmd/phases/upgrade/node/data_test.go index 6de8db579fd5a..6375a4cd04841 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/node/data_test.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/node/data_test.go @@ -42,3 +42,5 @@ func (t *testData) IgnorePreflightErrors() sets.Set[string] { return nil } func (t *testData) PatchesDir() string { return "" } func (t *testData) KubeConfigPath() string { return "" } func (t *testData) OutputWriter() io.Writer { return nil } +func (t *testData) KubeConfigDir() string { return "" } +func (t *testData) KubeletDir() string { return "" } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/node/preflight.go b/cmd/kubeadm/app/cmd/phases/upgrade/node/preflight.go index 58d06b7c284dd..a9985884e52a6 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/node/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/node/preflight.go @@ -53,7 +53,7 @@ func runPreflight(c workflow.RunData) error { if err := preflight.RunRootCheckOnly(data.IgnorePreflightErrors()); err != nil { return err } - if err := preflight.RunUpgradeChecks(data.IgnorePreflightErrors()); err != nil { + if err := preflight.RunUpgradeChecks(utilsexec.New(), data.InitCfg(), data.IgnorePreflightErrors()); err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/phases/upgrade/postupgrade.go b/cmd/kubeadm/app/cmd/phases/upgrade/postupgrade.go index f006000da18ae..cdb05a42c0868 100644 --- a/cmd/kubeadm/app/cmd/phases/upgrade/postupgrade.go +++ b/cmd/kubeadm/app/cmd/phases/upgrade/postupgrade.go @@ -20,6 +20,7 @@ package upgrade import ( "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" + "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" ) @@ -38,11 +39,24 @@ func NewPostUpgradePhase() workflow.Phase { } func runPostUpgrade(c workflow.RunData) error { - _, ok := c.(Data) + data, ok := c.(Data) if !ok { return errors.New("post-upgrade phase invoked with an invalid data struct") } // PLACEHOLDER: this phase should contain any release specific post-upgrade tasks. + // Rewrite the kubelet env file without unwanted flags to disk and print the remaining flags instead of dry-running. + // If not dry-running, the kubelet env file will be backed up to the /etc/kubernetes/tmp/ dir, so that it could be + // recovered if anything goes wrong. + unwantedFlags := []string{ + // The flag has been deprecated and no longer served a purpose in the kubelet as the logic was migrated to CRI. + // TODO: Remove it from this list in 1.36: https://github.com/kubernetes/kubeadm/issues/3108 + "pod-infra-container-image", + } + err := upgrade.RemoveKubeletArgsFromFile(data.KubeletDir(), data.KubeConfigDir(), unwantedFlags, data.DryRun(), data.OutputWriter()) + if err != nil { + return err + } + return nil } diff --git a/cmd/kubeadm/app/cmd/upgrade/apply.go b/cmd/kubeadm/app/cmd/upgrade/apply.go index c6c61ed3ac621..f940639844902 100644 --- a/cmd/kubeadm/app/cmd/upgrade/apply.go +++ b/cmd/kubeadm/app/cmd/upgrade/apply.go @@ -63,6 +63,7 @@ type applyData struct { nonInteractiveMode bool force bool dryRun bool + dryRunDir string etcdUpgrade bool renewCerts bool allowExperimentalUpgrades bool @@ -195,6 +196,14 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a return nil, cmdutil.TypeMismatchErr("dryRun", "bool") } + // If dry running creates a temporary directory for saving kubeadm generated files. + dryRunDir := "" + if *dryRun { + if dryRunDir, err = constants.GetDryRunDir(constants.EnvVarUpgradeDryRunDir, "kubeadm-upgrade-apply-dryrun", klog.Warningf); err != nil { + return nil, errors.Wrap(err, "could not create a temporary directory on dryrun") + } + } + etcdUpgrade, ok := cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.EtcdUpgrade, upgradeCfg.Apply.EtcdUpgrade, &applyFlags.etcdUpgrade).(*bool) if !ok { return nil, cmdutil.TypeMismatchErr("etcdUpgrade", "bool") @@ -273,6 +282,7 @@ func newApplyData(cmd *cobra.Command, args []string, applyFlags *applyFlags) (*a nonInteractiveMode: applyFlags.nonInteractiveMode, force: *force, dryRun: *dryRun, + dryRunDir: dryRunDir, etcdUpgrade: *etcdUpgrade, renewCerts: *renewCerts, allowExperimentalUpgrades: *allowExperimentalUpgrades, @@ -357,3 +367,19 @@ func (d *applyData) IsControlPlaneNode() bool { // `kubeadm upgrade apply` should always be executed on a control-plane node return true } + +// KubeConfigDir returns the Kubernetes configuration directory or the temporary directory if DryRun is true. +func (j *applyData) KubeConfigDir() string { + if j.dryRun { + return j.dryRunDir + } + return constants.KubernetesDir +} + +// KubeletDir returns the kubelet configuration directory or the temporary directory if DryRun is true. +func (j *applyData) KubeletDir() string { + if j.dryRun { + return j.dryRunDir + } + return constants.KubeletRunDirectory +} diff --git a/cmd/kubeadm/app/cmd/upgrade/node.go b/cmd/kubeadm/app/cmd/upgrade/node.go index 1e3cd71041e8a..1c8ad80049e11 100644 --- a/cmd/kubeadm/app/cmd/upgrade/node.go +++ b/cmd/kubeadm/app/cmd/upgrade/node.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4" @@ -63,6 +64,7 @@ type nodeData struct { etcdUpgrade bool renewCerts bool dryRun bool + dryRunDir string cfg *kubeadmapi.UpgradeConfiguration initCfg *kubeadmapi.InitConfiguration isControlPlaneNode bool @@ -185,6 +187,14 @@ func newNodeData(cmd *cobra.Command, nodeOptions *nodeOptions, out io.Writer) (* return nil, cmdutil.TypeMismatchErr("dryRun", "bool") } + // If dry running creates a temporary directory for saving kubeadm generated files. + dryRunDir := "" + if *dryRun { + if dryRunDir, err = constants.GetDryRunDir(constants.EnvVarUpgradeDryRunDir, "kubeadm-upgrade-node-dryrun", klog.Warningf); err != nil { + return nil, errors.Wrap(err, "could not create a temporary directory on dryrun") + } + } + printer := &output.TextPrinter{} client, err := getClient(nodeOptions.kubeConfigPath, *dryRun, printer) if err != nil { @@ -217,6 +227,7 @@ func newNodeData(cmd *cobra.Command, nodeOptions *nodeOptions, out io.Writer) (* return &nodeData{ cfg: upgradeCfg, dryRun: *dryRun, + dryRunDir: dryRunDir, initCfg: initCfg, client: client, isControlPlaneNode: isControlPlaneNode, @@ -282,3 +293,19 @@ func (d *nodeData) KubeConfigPath() string { func (d *nodeData) OutputWriter() io.Writer { return d.outputWriter } + +// KubeConfigDir returns the Kubernetes configuration directory or the temporary directory if DryRun is true. +func (j *nodeData) KubeConfigDir() string { + if j.dryRun { + return j.dryRunDir + } + return constants.KubernetesDir +} + +// KubeletDir returns the kubelet configuration directory or the temporary directory if DryRun is true. +func (j *nodeData) KubeletDir() string { + if j.dryRun { + return j.dryRunDir + } + return constants.KubeletRunDirectory +} diff --git a/cmd/kubeadm/app/cmd/upgrade/plan.go b/cmd/kubeadm/app/cmd/upgrade/plan.go index 2aed7fa1366ea..af73aec8e3b77 100644 --- a/cmd/kubeadm/app/cmd/upgrade/plan.go +++ b/cmd/kubeadm/app/cmd/upgrade/plan.go @@ -120,7 +120,7 @@ func runPlan(flagSet *pflag.FlagSet, flags *planFlags, args []string, printer ou return cmdutil.TypeMismatchErr("allowExperimentalUpgrades", "bool") } - availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, *allowExperimentalUpgrades, *allowRCUpgrades, client, printer) + availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, *allowExperimentalUpgrades, *allowRCUpgrades, printer) if err != nil { return errors.Wrap(err, "[upgrade/versions] FATAL") } diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index 4935f0530bfbc..fee98ece17c72 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -326,7 +326,7 @@ const ( MinExternalEtcdVersion = "3.5.24-0" // DefaultEtcdVersion indicates the default etcd version that kubeadm uses - DefaultEtcdVersion = "3.6.5-0" + DefaultEtcdVersion = "3.6.6-0" // Etcd defines variable used internally when referring to etcd component Etcd = "etcd" @@ -364,7 +364,7 @@ const ( CoreDNSImageName = "coredns" // CoreDNSVersion is the version of CoreDNS to be deployed if it is used - CoreDNSVersion = "v1.12.1" + CoreDNSVersion = "v1.13.1" // ClusterConfigurationKind is the string kind value for the ClusterConfiguration struct ClusterConfigurationKind = "ClusterConfiguration" @@ -497,11 +497,15 @@ var ( CurrentKubernetesVersion = getSkewedKubernetesVersion(0) // SupportedEtcdVersion lists officially supported etcd versions with corresponding Kubernetes releases + // If you are updating the versions in this map, make sure to also update: + // - MinExternalEtcdVersion: with the minimum etcd version from this map. + // - DefaultEtcdVersion: with etcd version used for the current k8s release (0). + // The maximum length of the map should be 2, as kubeadm supports a maximum skew of -1 + // with the control plane version. SupportedEtcdVersion = map[uint8]string{ - 31: "3.5.24-0", - 32: "3.5.24-0", - 33: "3.5.24-0", - 34: "3.6.5-0", + uint8(getSkewedKubernetesVersion(-2).Minor()): "3.5.24-0", + uint8(getSkewedKubernetesVersion(-1).Minor()): "3.6.6-0", + uint8(getSkewedKubernetesVersion(0).Minor()): "3.6.6-0", } // KubeadmCertsClusterRoleName sets the name for the ClusterRole that allows diff --git a/cmd/kubeadm/app/constants/constants_test.go b/cmd/kubeadm/app/constants/constants_test.go index 779ec5fda9a42..dea843890e169 100644 --- a/cmd/kubeadm/app/constants/constants_test.go +++ b/cmd/kubeadm/app/constants/constants_test.go @@ -98,17 +98,9 @@ func TestGetStaticPodFilepath(t *testing.T) { } } -func TestEtcdSupportedVersionLength(t *testing.T) { - const max = 4 - if len(SupportedEtcdVersion) > max { - t.Fatalf("SupportedEtcdVersion must not include more than %d versions", max) - } -} - func TestEtcdSupportedVersion(t *testing.T) { var supportedEtcdVersion = map[uint8]string{ - 16: "3.3.17-0", - 17: "3.4.3-0", + 17: "3.3.17-0", 18: "3.4.3-0", } var tests = []struct { @@ -136,13 +128,13 @@ func TestEtcdSupportedVersion(t *testing.T) { expectedError: false, }, { - kubernetesVersion: "v1.16.0", + kubernetesVersion: "1.17.2", expectedVersion: version.MustParseSemantic("3.3.17-0"), expectedWarning: false, expectedError: false, }, { - kubernetesVersion: "1.17.2", + kubernetesVersion: "1.18.1", expectedVersion: version.MustParseSemantic("3.4.3-0"), expectedWarning: false, expectedError: false, diff --git a/cmd/kubeadm/app/discovery/file/file.go b/cmd/kubeadm/app/discovery/file/file.go index 2cc7caeb269a2..536781b794b58 100644 --- a/cmd/kubeadm/app/discovery/file/file.go +++ b/cmd/kubeadm/app/discovery/file/file.go @@ -94,7 +94,7 @@ func ValidateConfigInfo(config *clientcmdapi.Config, discoveryTimeout time.Durat err = wait.PollUntilContextTimeout(context.Background(), constants.DiscoveryRetryInterval, discoveryTimeout, true, func(_ context.Context) (bool, error) { - clusterinfoCM, lastError = client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(context.TODO(), bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{}) + clusterinfoCM, lastError = client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(context.Background(), bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{}) if lastError != nil { if apierrors.IsForbidden(lastError) { // If the request fails with a forbidden error, the cluster admin has not granted access to the cluster info configmap for anonymous clients. diff --git a/cmd/kubeadm/app/features/features.go b/cmd/kubeadm/app/features/features.go index 5fcd7d7f415ae..e3fbe4bb6c998 100644 --- a/cmd/kubeadm/app/features/features.go +++ b/cmd/kubeadm/app/features/features.go @@ -45,9 +45,6 @@ const ( // RootlessControlPlane is expected to be in alpha in v1.22 RootlessControlPlane = "RootlessControlPlane" - - // WaitForAllControlPlaneComponents is expected to be alpha in v1.30 - WaitForAllControlPlaneComponents = "WaitForAllControlPlaneComponents" ) // InitFeatureGates are the default feature gates for the init command @@ -61,9 +58,8 @@ var InitFeatureGates = FeatureList{ DeprecationMessage: "Deprecated in favor of the core kubelet feature UserNamespacesSupport which is beta since 1.30." + " Once UserNamespacesSupport graduates to GA, kubeadm will start using it and RootlessControlPlane will be removed.", }, - WaitForAllControlPlaneComponents: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.GA, LockToDefault: true}}, - ControlPlaneKubeletLocalMode: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}}, - NodeLocalCRISocket: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}}, + ControlPlaneKubeletLocalMode: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.GA, LockToDefault: true}}, + NodeLocalCRISocket: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}}, } // Feature represents a feature being gated diff --git a/cmd/kubeadm/app/images/images.go b/cmd/kubeadm/app/images/images.go index a9f4700707c78..2db5084a35f34 100644 --- a/cmd/kubeadm/app/images/images.go +++ b/cmd/kubeadm/app/images/images.go @@ -63,22 +63,22 @@ func GetDNSImage(cfg *kubeadmapi.ClusterConfiguration) string { } // GetEtcdImage generates and returns the image for etcd -func GetEtcdImage(cfg *kubeadmapi.ClusterConfiguration) string { +func GetEtcdImage(cfg *kubeadmapi.ClusterConfiguration, supportedEtcdVersion map[uint8]string) string { // Etcd uses default image repository by default etcdImageRepository := cfg.ImageRepository // unless an override is specified if cfg.Etcd.Local != nil && cfg.Etcd.Local.ImageRepository != "" { etcdImageRepository = cfg.Etcd.Local.ImageRepository } - etcdImageTag := GetEtcdImageTag(cfg) + etcdImageTag := GetEtcdImageTag(cfg, supportedEtcdVersion) return GetGenericImage(etcdImageRepository, constants.Etcd, etcdImageTag) } // GetEtcdImageTag generates and returns the image tag for etcd -func GetEtcdImageTag(cfg *kubeadmapi.ClusterConfiguration) string { +func GetEtcdImageTag(cfg *kubeadmapi.ClusterConfiguration, supportedEtcdVersion map[uint8]string) string { // Etcd uses an imageTag that corresponds to the etcd version matching the Kubernetes version etcdImageTag := constants.DefaultEtcdVersion - etcdVersion, warning, err := constants.EtcdSupportedVersion(constants.SupportedEtcdVersion, cfg.KubernetesVersion) + etcdVersion, warning, err := constants.EtcdSupportedVersion(supportedEtcdVersion, cfg.KubernetesVersion) if err == nil { etcdImageTag = etcdVersion.String() } @@ -119,7 +119,7 @@ func GetControlPlaneImages(cfg *kubeadmapi.ClusterConfiguration) []string { // if etcd is not external then add the image as it will be required if cfg.Etcd.Local != nil { - images = append(images, GetEtcdImage(cfg)) + images = append(images, GetEtcdImage(cfg, constants.SupportedEtcdVersion)) } return images diff --git a/cmd/kubeadm/app/images/images_test.go b/cmd/kubeadm/app/images/images_test.go index 56f3f297a8db3..f7184bb0265aa 100644 --- a/cmd/kubeadm/app/images/images_test.go +++ b/cmd/kubeadm/app/images/images_test.go @@ -149,7 +149,7 @@ func TestGetEtcdImage(t *testing.T) { }, } for _, rt := range tests { - actual := GetEtcdImage(rt.cfg) + actual := GetEtcdImage(rt.cfg, constants.SupportedEtcdVersion) if actual != rt.expected { t.Errorf( "failed GetEtcdImage:\n\texpected: %s\n\t actual: %s", diff --git a/cmd/kubeadm/app/phases/addons/dns/dns_test.go b/cmd/kubeadm/app/phases/addons/dns/dns_test.go index 4d4186728bfe0..f1706973166c4 100644 --- a/cmd/kubeadm/app/phases/addons/dns/dns_test.go +++ b/cmd/kubeadm/app/phases/addons/dns/dns_test.go @@ -1454,7 +1454,7 @@ func TestDeployedDNSAddon(t *testing.T) { }, { name: "with digest", - image: "registry.k8s.io/coredns/coredns:v1.12.1@sha256:a0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e", + image: fmt.Sprintf("registry.k8s.io/coredns/coredns:%s@sha256:1391544c978029fcddc65068f6ad67f396e55585b664ecccd7fefba029b9b706", kubeadmconstants.CoreDNSVersion), deploymentSize: 1, wantVersion: kubeadmconstants.CoreDNSVersion, }, diff --git a/cmd/kubeadm/app/phases/controlplane/manifests.go b/cmd/kubeadm/app/phases/controlplane/manifests.go index bbb79d7bfe812..29556ebfd5bed 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests.go @@ -154,11 +154,9 @@ func CreateStaticPodFiles(manifestDir, patchesDir string, cfg *kubeadmapi.Cluste if features.Enabled(cfg.FeatureGates, features.RootlessControlPlane) { if isDryRun { fmt.Printf("[control-plane] Would update static pod manifest for %q to run run as non-root\n", componentName) - } else { - if usersAndGroups != nil { - if err := staticpodutil.RunComponentAsNonRoot(componentName, &spec, usersAndGroups, cfg); err != nil { - return errors.Wrapf(err, "failed to run component %q as non-root", componentName) - } + } else if usersAndGroups != nil { + if err := staticpodutil.RunComponentAsNonRoot(componentName, &spec, usersAndGroups, cfg); err != nil { + return errors.Wrapf(err, "failed to run component %q as non-root", componentName) } } } diff --git a/cmd/kubeadm/app/phases/etcd/local.go b/cmd/kubeadm/app/phases/etcd/local.go index b3728128a79da..e10aa9a76c877 100644 --- a/cmd/kubeadm/app/phases/etcd/local.go +++ b/cmd/kubeadm/app/phases/etcd/local.go @@ -205,8 +205,8 @@ func GetEtcdPodSpec(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A return staticpodutil.ComponentPod( v1.Container{ Name: kubeadmconstants.Etcd, - Command: getEtcdCommand(cfg, endpoint, nodeName, initialCluster), - Image: images.GetEtcdImage(cfg), + Command: getEtcdCommand(cfg, endpoint, nodeName, initialCluster, kubeadmconstants.SupportedEtcdVersion), + Image: images.GetEtcdImage(cfg, kubeadmconstants.SupportedEtcdVersion), ImagePullPolicy: v1.PullIfNotPresent, // Mount the etcd datadir path read-write so etcd can store data in a more persistent manner VolumeMounts: []v1.VolumeMount{ @@ -240,7 +240,7 @@ func GetEtcdPodSpec(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A } // getEtcdCommand builds the right etcd command from the given config object -func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, nodeName string, initialCluster []etcdutil.Member) []string { +func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, nodeName string, initialCluster []etcdutil.Member, supportedEtcdVersion map[uint8]string) []string { // localhost IP family should be the same that the AdvertiseAddress etcdLocalhostAddress := "127.0.0.1" if utilsnet.IsIPv6String(endpoint.AdvertiseAddress) { @@ -265,7 +265,7 @@ func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A {Name: "listen-metrics-urls", Value: fmt.Sprintf("http://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdMetricsPort)))}, } - etcdImageTag := images.GetEtcdImageTag(cfg) + etcdImageTag := images.GetEtcdImageTag(cfg, supportedEtcdVersion) if etcdVersion, err := version.ParseSemantic(etcdImageTag); err == nil && etcdVersion.AtLeast(version.MustParseSemantic("3.6.0")) { // Arguments used by Etcd 3.6.0+. // TODO: Start always using these once kubeadm only supports etcd >= 3.6.0 for all its supported k8s versions. diff --git a/cmd/kubeadm/app/phases/etcd/local_test.go b/cmd/kubeadm/app/phases/etcd/local_test.go index 16cd66ec948ea..aa0bc2d56c19e 100644 --- a/cmd/kubeadm/app/phases/etcd/local_test.go +++ b/cmd/kubeadm/app/phases/etcd/local_test.go @@ -285,14 +285,15 @@ func TestCreateLocalEtcdStaticPodManifestFileWithPatches(t *testing.T) { func TestGetEtcdCommand(t *testing.T) { var tests = []struct { - name string - advertiseAddress string - k8sVersion string - etcdImageTag string - nodeName string - extraArgs []kubeadmapi.Arg - initialCluster []etcdutil.Member - expected []string + name string + advertiseAddress string + k8sVersion string + etcdImageTag string + nodeName string + extraArgs []kubeadmapi.Arg + initialCluster []etcdutil.Member + supportedEtcdVersion map[uint8]string + expected []string }{ { name: "Default args - with empty etcd initial cluster", @@ -416,6 +417,10 @@ func TestGetEtcdCommand(t *testing.T) { advertiseAddress: "1.2.3.4", k8sVersion: "1.33.0", nodeName: "bar", + supportedEtcdVersion: map[uint8]string{ + 33: "3.5.24", + 34: "3.6.5", + }, expected: []string{ "etcd", "--name=bar", @@ -514,7 +519,10 @@ func TestGetEtcdCommand(t *testing.T) { }, }, } - actual := getEtcdCommand(cfg, endpoint, rt.nodeName, rt.initialCluster) + if len(rt.supportedEtcdVersion) == 0 { + rt.supportedEtcdVersion = kubeadmconstants.SupportedEtcdVersion + } + actual := getEtcdCommand(cfg, endpoint, rt.nodeName, rt.initialCluster, rt.supportedEtcdVersion) sort.Strings(actual) sort.Strings(rt.expected) if !reflect.DeepEqual(actual, rt.expected) { diff --git a/cmd/kubeadm/app/phases/kubelet/flags.go b/cmd/kubeadm/app/phases/kubelet/flags.go index ed4e7b9a2cbf2..8abd742f8f68d 100644 --- a/cmd/kubeadm/app/phases/kubelet/flags.go +++ b/cmd/kubeadm/app/phases/kubelet/flags.go @@ -28,14 +28,12 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/features" - "k8s.io/kubernetes/cmd/kubeadm/app/images" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" ) type kubeletFlagsOpts struct { nodeRegOpts *kubeadmapi.NodeRegistrationOptions - pauseImage string registerTaintsUsingFlags bool // TODO: remove this field once the feature NodeLocalCRISocket is GA. criSocket string @@ -64,7 +62,6 @@ func GetNodeNameAndHostname(cfg *kubeadmapi.NodeRegistrationOptions) (string, st func WriteKubeletDynamicEnvFile(cfg *kubeadmapi.ClusterConfiguration, nodeReg *kubeadmapi.NodeRegistrationOptions, registerTaintsUsingFlags bool, kubeletDir string) error { flagOpts := kubeletFlagsOpts{ nodeRegOpts: nodeReg, - pauseImage: images.GetPauseImage(cfg), registerTaintsUsingFlags: registerTaintsUsingFlags, criSocket: nodeReg.CRISocket, } @@ -92,12 +89,6 @@ func buildKubeletArgsCommon(opts kubeletFlagsOpts) []kubeadmapi.Arg { kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "container-runtime-endpoint", Value: opts.criSocket}) } - // This flag passes the pod infra container image (e.g. "pause" image) to the kubelet - // and prevents its garbage collection - if opts.pauseImage != "" { - kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "pod-infra-container-image", Value: opts.pauseImage}) - } - if opts.registerTaintsUsingFlags && opts.nodeRegOpts.Taints != nil && len(opts.nodeRegOpts.Taints) > 0 { taintStrs := []string{} for _, taint := range opts.nodeRegOpts.Taints { @@ -163,7 +154,7 @@ func ReadKubeletDynamicEnvFile(kubeletEnvFilePath string) ([]string, error) { // Split the flags string by whitespace to get individual arguments. trimmedFlags := strings.Fields(flags) if len(trimmedFlags) == 0 { - return nil, errors.Errorf("no flags found in file %q", kubeletEnvFilePath) + return nil, nil } var updatedFlags []string diff --git a/cmd/kubeadm/app/phases/kubelet/flags_test.go b/cmd/kubeadm/app/phases/kubelet/flags_test.go index b159b35021dc7..d2134086190ac 100644 --- a/cmd/kubeadm/app/phases/kubelet/flags_test.go +++ b/cmd/kubeadm/app/phases/kubelet/flags_test.go @@ -75,18 +75,6 @@ func TestBuildKubeletArgs(t *testing.T) { {Name: "register-with-taints", Value: "foo=bar:baz,key=val:eff"}, }, }, - { - name: "pause image is set", - opts: kubeletFlagsOpts{ - nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{}, - criSocket: "unix:///var/run/containerd/containerd.sock", - pauseImage: "registry.k8s.io/pause:ver", - }, - expected: []kubeadmapi.Arg{ - {Name: "container-runtime-endpoint", Value: "unix:///var/run/containerd/containerd.sock"}, - {Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:ver"}, - }, - }, } for _, test := range tests { @@ -200,15 +188,15 @@ func TestReadKubeadmFlags(t *testing.T) { }{ { name: "valid kubeadm flags with container-runtime-endpoint", - fileContent: `KUBELET_KUBEADM_ARGS="--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock --pod-infra-container-image=registry.k8s.io/pause:1.0"`, - expectedValue: []string{"--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock", "--pod-infra-container-image=registry.k8s.io/pause:1.0"}, + fileContent: `KUBELET_KUBEADM_ARGS="--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"`, + expectedValue: []string{"--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock"}, expectError: false, }, { - name: "no container-runtime-endpoint found", - fileContent: `KUBELET_KUBEADM_ARGS="--pod-infra-container-image=registry.k8s.io/pause:1.0"`, - expectedValue: []string{"--pod-infra-container-image=registry.k8s.io/pause:1.0"}, - expectError: true, + name: "has KUBELET_KUBEADM_ARGS line but no args", + fileContent: `KUBELET_KUBEADM_ARGS=""`, + expectedValue: nil, + expectError: false, }, { name: "no KUBELET_KUBEADM_ARGS line", @@ -260,8 +248,8 @@ func TestReadKubeadmFlags(t *testing.T) { } value, err := ReadKubeletDynamicEnvFile(tmpFile.Name()) - if !tt.expectError && err != nil { - t.Errorf("Unexpected error: %v", err) + if tt.expectError != (err != nil) { + t.Errorf("Expect error: %v, got: %v, error: %v", tt.expectError, err != nil, err) } assert.Equal(t, tt.expectedValue, value) diff --git a/cmd/kubeadm/app/phases/upgrade/compute.go b/cmd/kubeadm/app/phases/upgrade/compute.go index 3463b259d7eae..806de8dae83d3 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute.go +++ b/cmd/kubeadm/app/phases/upgrade/compute.go @@ -21,11 +21,9 @@ import ( "strings" versionutil "k8s.io/apimachinery/pkg/util/version" - clientset "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" - "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) @@ -74,7 +72,7 @@ type ClusterState struct { // GetAvailableUpgrades fetches all versions from the specified VersionGetter and computes which // kinds of upgrades can be performed -func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, client clientset.Interface, printer output.Printer) ([]Upgrade, error) { +func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesAllowed, rcUpgradesAllowed bool, printer output.Printer) ([]Upgrade, error) { printer.Printf("[upgrade] Fetching available versions to upgrade to\n") // Collect the upgrades kubeadm can do in this list @@ -145,7 +143,7 @@ func GetAvailableUpgrades(versionGetterImpl VersionGetter, experimentalUpgradesA } isExternalEtcd := len(etcdVersions) == 0 - dnsVersion, err := dns.DeployedDNSAddon(client) + dnsVersion, err := versionGetterImpl.DNSAddonVersion() if err != nil { return nil, err } diff --git a/cmd/kubeadm/app/phases/upgrade/compute_test.go b/cmd/kubeadm/app/phases/upgrade/compute_test.go index f6255728d24cb..7745b738d0aaf 100644 --- a/cmd/kubeadm/app/phases/upgrade/compute_test.go +++ b/cmd/kubeadm/app/phases/upgrade/compute_test.go @@ -17,19 +17,13 @@ limitations under the License. package upgrade import ( - "context" "fmt" "strings" "testing" "github.com/google/go-cmp/cmp" - apps "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" versionutil "k8s.io/apimachinery/pkg/util/version" - clientsetfake "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" "k8s.io/kubernetes/cmd/kubeadm/app/util/output" @@ -46,6 +40,8 @@ type fakeVersionGetter struct { componentVersion string etcdVersion string isExternalEtcd bool + coreDNSVersion string + coreDNSError error } var _ VersionGetter = &fakeVersionGetter{} @@ -117,6 +113,10 @@ func (f *fakeVersionGetter) ComponentVersions(name string) (map[string][]string, }, nil } +func (f *fakeVersionGetter) DNSAddonVersion() (string, error) { + return f.coreDNSVersion, f.coreDNSError +} + const fakeCurrentEtcdVersion = "3.1.12" func getEtcdVersion(v *versionutil.Version) string { @@ -158,22 +158,20 @@ func TestGetAvailableUpgrades(t *testing.T) { expectedUpgrades []Upgrade allowExperimental, allowRCs bool errExpected bool - beforeDNSVersion string - deployDNSFailed bool }{ { name: "no action needed, already up-to-date", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: v1Y0.String(), - kubeletVersion: v1Y0.String(), - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: v1Y0.String(), + kubeletVersion: v1Y0.String(), + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, errExpected: false, @@ -181,16 +179,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "get component version failed", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: "", - kubeletVersion: v1Y0.String(), - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: "", + kubeletVersion: v1Y0.String(), + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, errExpected: true, @@ -198,16 +196,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "there is version information about multiple components", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: "multiVersion", - kubeletVersion: v1Y0.String(), - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: "multiVersion", + kubeletVersion: v1Y0.String(), + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, errExpected: true, @@ -215,16 +213,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "get kubeadm version failed", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: v1Y0.String(), - kubeletVersion: v1Y0.String(), - kubeadmVersion: "", - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: v1Y0.String(), + kubeletVersion: v1Y0.String(), + kubeadmVersion: "", + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, errExpected: true, @@ -232,16 +230,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "get kubelet version failed", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: v1Y0.String(), - kubeletVersion: "", - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: v1Y0.String(), + kubeletVersion: "", + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, errExpected: true, @@ -249,16 +247,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "deploy DNS failed", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: v1Y0.String(), - kubeletVersion: v1Y0.String(), - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: v1Y0.String(), + kubeletVersion: v1Y0.String(), + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: v1Y0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: false, @@ -267,17 +265,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "get stable version from CI label failed", vg: &fakeVersionGetter{ - clusterVersion: v1Y0.String(), - componentVersion: v1Y0.String(), - kubeletVersion: v1Y0.String(), - kubeadmVersion: v1Y0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y0.String(), + componentVersion: v1Y0.String(), + kubeletVersion: v1Y0.String(), + kubeadmVersion: v1Y0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y0.String(), stableVersion: "", + coreDNSVersion: "", + coreDNSError: errors.New("multiple DNS addon deployment"), }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, - deployDNSFailed: true, expectedUpgrades: nil, allowExperimental: false, errExpected: true, @@ -285,16 +282,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "simple patch version upgrade", vg: &fakeVersionGetter{ - clusterVersion: v1Y1.String(), - componentVersion: v1Y1.String(), - kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane - kubeadmVersion: v1Y2.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y1.String(), + componentVersion: v1Y1.String(), + kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane + kubeadmVersion: v1Y2.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y3.String(), stableVersion: v1Y3.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), @@ -330,16 +327,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "simple patch version upgrade with external etcd", vg: &fakeVersionGetter{ - clusterVersion: v1Y1.String(), - componentVersion: v1Y1.String(), - kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane - kubeadmVersion: v1Y2.String(), - isExternalEtcd: true, - + clusterVersion: v1Y1.String(), + componentVersion: v1Y1.String(), + kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane + kubeadmVersion: v1Y2.String(), + isExternalEtcd: true, stablePatchVersion: v1Y3.String(), stableVersion: v1Y3.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), @@ -375,16 +372,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "no version provided to offline version getter does not change behavior", vg: NewOfflineVersionGetter(&fakeVersionGetter{ - clusterVersion: v1Y1.String(), - componentVersion: v1Y1.String(), - kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane - kubeadmVersion: v1Y2.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y1.String(), + componentVersion: v1Y1.String(), + kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane + kubeadmVersion: v1Y2.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y3.String(), stableVersion: v1Y3.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, ""), - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), @@ -420,16 +417,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "minor version upgrade only", vg: &fakeVersionGetter{ - clusterVersion: v1Y1.String(), - componentVersion: v1Y1.String(), - kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane - kubeadmVersion: v1Z0.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y1.String(), + componentVersion: v1Y1.String(), + kubeletVersion: v1Y1.String(), // the kubelet are on the same version as the control plane + kubeadmVersion: v1Z0.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y1.String(), stableVersion: v1Z0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "stable version", @@ -465,16 +462,16 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "both minor version upgrade and patch version upgrade available", vg: &fakeVersionGetter{ - clusterVersion: v1Y3.String(), - componentVersion: v1Y3.String(), - kubeletVersion: v1Y3.String(), // the kubelet are on the same version as the control plane - kubeadmVersion: v1Y5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y3.String(), + componentVersion: v1Y3.String(), + kubeletVersion: v1Y3.String(), // the kubelet are on the same version as the control plane + kubeadmVersion: v1Y5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y5.String(), stableVersion: v1Z1.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), @@ -537,17 +534,17 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "allow experimental upgrades, but no upgrade available", vg: &fakeVersionGetter{ - clusterVersion: v1Z0alpha2.String(), - componentVersion: v1Z0alpha2.String(), - kubeletVersion: v1Y5.String(), - kubeadmVersion: v1Y5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Z0alpha2.String(), + componentVersion: v1Z0alpha2.String(), + kubeletVersion: v1Y5.String(), + kubeadmVersion: v1Y5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y5.String(), stableVersion: v1Y5.String(), latestVersion: v1Z0alpha2.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: nil, allowExperimental: true, errExpected: false, @@ -555,17 +552,17 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "upgrade to an unstable version should be supported", vg: &fakeVersionGetter{ - clusterVersion: v1Y5.String(), - componentVersion: v1Y5.String(), - kubeletVersion: v1Y5.String(), - kubeadmVersion: v1Y5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Y5.String(), + componentVersion: v1Y5.String(), + kubeletVersion: v1Y5.String(), + kubeadmVersion: v1Y5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y5.String(), stableVersion: v1Y5.String(), latestVersion: v1Z0alpha2.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "experimental version", @@ -601,17 +598,17 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "upgrade from an unstable version to an unstable version should be supported", vg: &fakeVersionGetter{ - clusterVersion: v1Z0alpha1.String(), - componentVersion: v1Z0alpha1.String(), - kubeletVersion: v1Y5.String(), - kubeadmVersion: v1Y5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1Z0alpha1.String(), + componentVersion: v1Z0alpha1.String(), + kubeletVersion: v1Y5.String(), + kubeadmVersion: v1Y5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1Y5.String(), stableVersion: v1Y5.String(), latestVersion: v1Z0alpha2.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "experimental version", @@ -647,18 +644,18 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "v1.X.0-alpha.0 should be ignored", vg: &fakeVersionGetter{ - clusterVersion: v1X5.String(), - componentVersion: v1X5.String(), - kubeletVersion: v1X5.String(), - kubeadmVersion: v1X5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1X5.String(), + componentVersion: v1X5.String(), + kubeletVersion: v1X5.String(), + kubeadmVersion: v1X5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1X5.String(), stableVersion: v1X5.String(), latestDevBranchVersion: v1Z0beta1.String(), latestVersion: v1Y0alpha0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "experimental version", @@ -694,18 +691,18 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "upgrade to an RC version should be supported", vg: &fakeVersionGetter{ - clusterVersion: v1X5.String(), - componentVersion: v1X5.String(), - kubeletVersion: v1X5.String(), - kubeadmVersion: v1X5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1X5.String(), + componentVersion: v1X5.String(), + kubeletVersion: v1X5.String(), + kubeadmVersion: v1X5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1X5.String(), stableVersion: v1X5.String(), latestDevBranchVersion: v1Z0rc1.String(), latestVersion: v1Y0alpha1.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "release candidate version", @@ -741,18 +738,18 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "it is possible (but very uncommon) that the latest version from the previous branch is an rc and the current latest version is alpha.0. In that case, show the RC", vg: &fakeVersionGetter{ - clusterVersion: v1X5.String(), - componentVersion: v1X5.String(), - kubeletVersion: v1X5.String(), - kubeadmVersion: v1X5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1X5.String(), + componentVersion: v1X5.String(), + kubeletVersion: v1X5.String(), + kubeadmVersion: v1X5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1X5.String(), stableVersion: v1X5.String(), latestDevBranchVersion: v1Z0rc1.String(), latestVersion: v1Y0alpha0.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "experimental version", // Note that this is considered an experimental version in this uncommon scenario @@ -788,18 +785,18 @@ func TestGetAvailableUpgrades(t *testing.T) { { name: "upgrade to an RC version should be supported. There may also be an even newer unstable version.", vg: &fakeVersionGetter{ - clusterVersion: v1X5.String(), - componentVersion: v1X5.String(), - kubeletVersion: v1X5.String(), - kubeadmVersion: v1X5.String(), - etcdVersion: fakeCurrentEtcdVersion, - + clusterVersion: v1X5.String(), + componentVersion: v1X5.String(), + kubeletVersion: v1X5.String(), + kubeadmVersion: v1X5.String(), + etcdVersion: fakeCurrentEtcdVersion, stablePatchVersion: v1X5.String(), stableVersion: v1X5.String(), latestDevBranchVersion: v1Z0rc1.String(), latestVersion: v1Y0alpha1.String(), + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: "release candidate version", @@ -868,8 +865,9 @@ func TestGetAvailableUpgrades(t *testing.T) { kubeletVersion: v1Y0.String(), kubeadmVersion: v1Y1.String(), etcdVersion: fakeCurrentEtcdVersion, + coreDNSVersion: fakeCurrentCoreDNSVersion, + coreDNSError: nil, }, v1Z1.String()), - beforeDNSVersion: fakeCurrentCoreDNSVersion, expectedUpgrades: []Upgrade{ { Description: fmt.Sprintf("version in the v%d.%d series", v1Y0.Major(), v1Y0.Minor()), @@ -906,12 +904,7 @@ func TestGetAvailableUpgrades(t *testing.T) { // Kubernetes release. for _, rt := range tests { t.Run(rt.name, func(t *testing.T) { - - dnsName := constants.CoreDNSDeploymentName - - client := newMockClientForTest(t, dnsName, rt.beforeDNSVersion, rt.deployDNSFailed) - - actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, client, &output.TextPrinter{}) + actualUpgrades, actualErr := GetAvailableUpgrades(rt.vg, rt.allowExperimental, rt.allowRCs, &output.TextPrinter{}) if diff := cmp.Diff(rt.expectedUpgrades, actualUpgrades); len(diff) > 0 { t.Errorf("failed TestGetAvailableUpgrades\n\texpected upgrades:\n%v\n\tgot:\n%v\n\tdiff:\n%v", rt.expectedUpgrades, actualUpgrades, diff) } @@ -1100,42 +1093,3 @@ func TestGetSuggestedEtcdVersion(t *testing.T) { }) } } - -func newMockClientForTest(t *testing.T, dnsName string, dnsVersion string, multiDNS bool) *clientsetfake.Clientset { - client := clientsetfake.NewSimpleClientset() - createDeployment := func(name string) { - _, err := client.AppsV1().Deployments(metav1.NamespaceSystem).Create(context.TODO(), &apps.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: dnsName + name, - Namespace: "kube-system", - Labels: map[string]string{ - "k8s-app": "kube-dns", - }, - }, - Spec: apps.DeploymentSpec{ - Template: v1.PodTemplateSpec{ - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: "test:" + dnsVersion + name, - }, - }, - }, - }, - }, - }, metav1.CreateOptions{}) - if err != nil { - t.Fatalf("error creating deployment: %v", err) - } - } - - createDeployment("") - if multiDNS { - createDeployment("dns2") - } - return client -} diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade.go b/cmd/kubeadm/app/phases/upgrade/postupgrade.go index 5ffe1cdf2e728..53132b6b7cb5a 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade.go @@ -22,6 +22,7 @@ import ( "io" "os" "path/filepath" + "slices" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -95,21 +96,9 @@ func UnupgradedControlPlaneInstances(client clientset.Interface, nodeName string } // WriteKubeletConfigFiles writes the kubelet config file to disk, but first creates a backup of any existing one. -func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir string, patchesDir string, dryRun bool, out io.Writer) error { - var ( - err error - kubeletDir = kubeadmconstants.KubeletRunDirectory - ) - // If dry-running, this will return a directory under /etc/kubernetes/tmp or kubeletConfigDir. - if dryRun { - kubeletDir, err = kubeadmconstants.CreateTempDir(kubeletConfigDir, "kubeadm-upgrade-dryrun") - } - if err != nil { - // The error here should never occur in reality, would only be thrown if /tmp doesn't exist on the machine. - return err - } - // Create a copy of the kubelet config file in the /etc/kubernetes/tmp or kubeletConfigDir. - backupDir, err := kubeadmconstants.CreateTempDir(kubeletConfigDir, "kubeadm-kubelet-config") +func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletDir string, kubeConfigDir string, patchesDir string, dryRun bool, out io.Writer) error { + // Create a copy of the kubelet config file in the /etc/kubernetes/tmp or kubeConfigDir. + backupDir, err := kubeadmconstants.CreateTimestampDir(kubeConfigDir, "kubeadm-kubelet-config") if err != nil { return err } @@ -120,13 +109,13 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir dest := filepath.Join(backupDir, kubeadmconstants.KubeletConfigurationFileName) if !dryRun { - fmt.Printf("[upgrade] Backing up kubelet config file to %s\n", dest) + _, _ = fmt.Fprintf(out, "[upgrade] Backing up kubelet config file to %s\n", dest) err := kubeadmutil.CopyFile(src, dest) if err != nil { return errors.Wrap(err, "error backing up the kubelet config file") } } else { - fmt.Printf("[dryrun] Would back up kubelet config file to %s\n", dest) + _, _ = fmt.Fprintf(out, "[dryrun] Would back up kubelet config file to %s\n", dest) } if features.Enabled(cfg.FeatureGates, features.NodeLocalCRISocket) { @@ -154,7 +143,7 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir } } } else if dryRun { - fmt.Fprintf(os.Stdout, "[dryrun] would read the flag --container-runtime-endpoint value from %q, which is missing. "+ + _, _ = fmt.Fprintf(out, "[dryrun] would read the flag --container-runtime-endpoint value from %q, which is missing. "+ "Using default socket %q instead", kubeadmconstants.KubeletEnvFileName, kubeadmconstants.DefaultCRISocket) containerRuntimeEndpoint = kubeadmconstants.DefaultCRISocket } else { @@ -170,7 +159,7 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir } if dryRun { // Print what contents would be written - err = dryrunutil.PrintDryRunFile(kubeadmconstants.KubeletInstanceConfigurationFileName, kubeletDir, kubeadmconstants.KubeletRunDirectory, os.Stdout) + err = dryrunutil.PrintDryRunFile(kubeadmconstants.KubeletInstanceConfigurationFileName, kubeletDir, kubeadmconstants.KubeletRunDirectory, out) if err != nil { return errors.Wrap(err, "error printing kubelet instance configuration file on dryrun") } @@ -184,7 +173,7 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir } if dryRun { // Print what contents would be written - err := dryrunutil.PrintDryRunFile(kubeadmconstants.KubeletConfigurationFileName, kubeletDir, kubeadmconstants.KubeletRunDirectory, os.Stdout) + err := dryrunutil.PrintDryRunFile(kubeadmconstants.KubeletConfigurationFileName, kubeletDir, kubeadmconstants.KubeletRunDirectory, out) if err != nil { return errors.Wrap(err, "error printing kubelet configuration file on dryrun") } @@ -192,10 +181,59 @@ func WriteKubeletConfigFiles(cfg *kubeadmapi.InitConfiguration, kubeletConfigDir return nil } -// UpdateKubeletKubeconfigServer changes the Server URL in the kubelets kubeconfig if necessary depending on the -// ControlPlaneKubeletLocalMode feature gate. -// TODO: remove this function once ControlPlaneKubeletLocalMode goes GA and is hardcoded to be enabled by default: -// https://github.com/kubernetes/kubeadm/issues/2271 +// RemoveKubeletArgsFromFile removes unwanted kubelet flags from the existing KubeletEnvFile file, +// but first creates a backup of the existing file. +func RemoveKubeletArgsFromFile(kubeletDir string, kubeConfigDir string, unwantedFlags []string, dryRun bool, out io.Writer) error { + // If there are no unwanted flags, do nothing. + if len(unwantedFlags) == 0 { + return nil + } + + // For dry run mode, we can not access the real kubelet env file in the dry-run directory, so we only print the unwanted flags instead. + if dryRun { + _, _ = fmt.Fprintf(out, "[dryrun] Would remove unwanted kubelet flags %v from %s\n", unwantedFlags, kubeadmconstants.KubeletEnvFileName) + return nil + } + + // Create a copy of the KubeletEnvFile in the /etc/kubernetes/tmp or kubeConfigDir. + backupDir, err := kubeadmconstants.CreateTempDir(kubeConfigDir, "kubeadm-kubelet-env") + if err != nil { + return err + } + klog.Warningf("Using temporary directory %s for kubelet env file. To override it set the environment variable %s", + backupDir, kubeadmconstants.EnvVarUpgradeDryRunDir) + + src := filepath.Join(kubeletDir, kubeadmconstants.KubeletEnvFileName) + dest := filepath.Join(backupDir, kubeadmconstants.KubeletEnvFileName) + + _, _ = fmt.Fprintf(out, "[upgrade] Backing up kubelet env file to %s\n", dest) + err = kubeadmutil.CopyFile(src, dest) + if err != nil { + return errors.Wrap(err, "error backing up the kubelet env file") + } + + var kubeletFlags []kubeadmapi.Arg + command, err := kubeletphase.ReadKubeletDynamicEnvFile(src) + if err != nil { + return errors.Wrap(err, "error reading kubelet env file") + } + args := kubeadmutil.ArgumentsFromCommand(command) + for _, arg := range args { + if slices.Contains(unwantedFlags, arg.Name) { + continue + } + kubeletFlags = append(kubeletFlags, arg) + } + // Rewrite env file if needed + if len(args) != len(kubeletFlags) { + if err := kubeletphase.WriteKubeletArgsToFile(kubeletFlags, nil, kubeletDir); err != nil { + return errors.Wrap(err, "error writing kubelet env file") + } + } + return nil +} + +// UpdateKubeletKubeconfigServer changes the Server URL in the kubelet's kubeconfig if necessary. func UpdateKubeletKubeconfigServer(cfg *kubeadmapi.InitConfiguration, dryRun bool) error { kubeletKubeConfigFilePath := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName) @@ -228,21 +266,12 @@ func UpdateKubeletKubeconfigServer(cfg *kubeadmapi.InitConfiguration, dryRun boo } var targetServer string - if features.Enabled(cfg.FeatureGates, features.ControlPlaneKubeletLocalMode) { - // Skip changing kubeconfig file if Server does not match the ControlPlaneEndpoint. - if config.Clusters[configContext.Cluster].Server != controlPlaneAPIEndpoint || controlPlaneAPIEndpoint == localAPIEndpoint { - klog.V(2).Infof("Skipping update of the Server URL in %s, because it's already not equal to %q or already matches the localAPIEndpoint", kubeletKubeConfigFilePath, controlPlaneAPIEndpoint) - return nil - } - targetServer = localAPIEndpoint - } else { - // Skip changing kubeconfig file if Server does not match the localAPIEndpoint. - if config.Clusters[configContext.Cluster].Server != localAPIEndpoint { - klog.V(2).Infof("Skipping update of the Server URL in %s, because it already matches the controlPlaneAPIEndpoint", kubeletKubeConfigFilePath) - return nil - } - targetServer = controlPlaneAPIEndpoint + // Skip changing kubeconfig file if Server does not match the ControlPlaneEndpoint. + if config.Clusters[configContext.Cluster].Server != controlPlaneAPIEndpoint || controlPlaneAPIEndpoint == localAPIEndpoint { + klog.V(2).Infof("Skipping update of the Server URL in %s, because it's already not equal to %q or already matches the localAPIEndpoint", kubeletKubeConfigFilePath, controlPlaneAPIEndpoint) + return nil } + targetServer = localAPIEndpoint if dryRun { fmt.Printf("[dryrun] Would change the Server URL from %q to %q in %s and try to restart kubelet\n", config.Clusters[configContext.Cluster].Server, targetServer, kubeletKubeConfigFilePath) diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go index 6dc145c71ab45..2178e7d068f48 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade_test.go @@ -17,6 +17,7 @@ limitations under the License. package upgrade import ( + "io" "os" "path/filepath" "reflect" @@ -26,12 +27,14 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/diff" errorsutil "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/kubernetes/fake" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" "k8s.io/kubernetes/cmd/kubeadm/app/constants" + kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" ) @@ -142,13 +145,111 @@ func TestWriteKubeletConfigFiles(t *testing.T) { }, } for _, tc := range testCases { - err := WriteKubeletConfigFiles(tc.cfg, tempDir, tc.patchesDir, true, os.Stdout) + err := WriteKubeletConfigFiles(tc.cfg, tempDir, tempDir, tc.patchesDir, true, os.Stdout) if (err != nil) != tc.expectedError { t.Fatalf("expected error: %v, got: %v, error: %v", tc.expectedError, err != nil, err) } } } +func TestRemoveKubeletArgsFromFile(t *testing.T) { + testCases := []struct { + name string + kubeletFlags []kubeadmapi.Arg + unwantedFlags []string + wantErr bool + wantFileContent string + }{ + { + name: "remove an existing flag", + kubeletFlags: []kubeadmapi.Arg{ + {Name: "node-ip", Value: "172.18.0.2"}, + {Name: "node-labels", Value: ""}, + {Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:ver"}, + {Name: "provider-id", Value: "kind://docker/kind/kind-control-plane"}, + }, + unwantedFlags: []string{ + "pod-infra-container-image", + }, + wantErr: false, + wantFileContent: `KUBELET_KUBEADM_ARGS="--node-ip=172.18.0.2 --node-labels= --provider-id=kind://docker/kind/kind-control-plane" +`, + }, + { + name: "remove multiple existing flags", + kubeletFlags: []kubeadmapi.Arg{ + {Name: "node-ip", Value: "172.18.0.2"}, + {Name: "node-labels", Value: ""}, + {Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:ver"}, + {Name: "provider-id", Value: "kind://docker/kind/kind-control-plane"}, + }, + unwantedFlags: []string{ + "pod-infra-container-image", + "node-labels", + }, + wantErr: false, + wantFileContent: `KUBELET_KUBEADM_ARGS="--node-ip=172.18.0.2 --provider-id=kind://docker/kind/kind-control-plane" +`, + }, + { + name: "remove non-existing flags", + kubeletFlags: []kubeadmapi.Arg{ + {Name: "node-ip", Value: "172.18.0.2"}, + {Name: "node-labels", Value: ""}, + {Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:ver"}, + {Name: "provider-id", Value: "kind://docker/kind/kind-control-plane"}, + }, + unwantedFlags: []string{ + "foo", + }, + wantErr: false, + wantFileContent: `KUBELET_KUBEADM_ARGS="--node-ip=172.18.0.2 --node-labels= --pod-infra-container-image=registry.k8s.io/pause:ver --provider-id=kind://docker/kind/kind-control-plane" +`, + }, + { + name: "remove multiple flags mixed with non-existing and existing flags", + kubeletFlags: []kubeadmapi.Arg{ + {Name: "node-ip", Value: "172.18.0.2"}, + {Name: "node-labels", Value: ""}, + {Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:ver"}, + {Name: "provider-id", Value: "kind://docker/kind/kind-control-plane"}, + }, + unwantedFlags: []string{ + "pod-infra-container-image", + "foo", + }, + wantErr: false, + wantFileContent: `KUBELET_KUBEADM_ARGS="--node-ip=172.18.0.2 --node-labels= --provider-id=kind://docker/kind/kind-control-plane" +`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + tempDir := t.TempDir() + + err := kubeletphase.WriteKubeletArgsToFile(tc.kubeletFlags, nil, tempDir) + if err != nil { + t.Fatalf("Failed to write kubeadm-flags.env file: %v", err) + } + + err = RemoveKubeletArgsFromFile(tempDir, tempDir, tc.unwantedFlags, false, io.Discard) + if (err != nil) != tc.wantErr { + t.Fatalf("expected error: %v, got: %v, error: %v", tc.wantErr, err != nil, err) + } + + kubeletEnvFilePath := filepath.Join(tempDir, constants.KubeletEnvFileName) + fileContent, err := os.ReadFile(kubeletEnvFilePath) + if err != nil { + t.Fatalf("Failed to read kubelet.env file: %v", err) + } + if gotOut := string(fileContent); gotOut != tc.wantFileContent { + t.Fatalf("Actual modified content of RemoveKubeletArgsFromFile() does not match expected.\nActual: %v\nExpected: %v\n, Diff: %v", gotOut, tc.wantFileContent, diff.Diff(gotOut, tc.wantFileContent)) + } + }) + } +} + func TestUnupgradedControlPlaneInstances(t *testing.T) { testCases := []struct { name string diff --git a/cmd/kubeadm/app/phases/upgrade/versiongetter.go b/cmd/kubeadm/app/phases/upgrade/versiongetter.go index ef07a463cf9cb..f1b75c1547152 100644 --- a/cmd/kubeadm/app/phases/upgrade/versiongetter.go +++ b/cmd/kubeadm/app/phases/upgrade/versiongetter.go @@ -28,6 +28,7 @@ import ( "k8s.io/component-base/version" "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/dns" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" "k8s.io/kubernetes/cmd/kubeadm/app/util/image" @@ -46,6 +47,8 @@ type VersionGetter interface { KubeletVersions() (map[string][]string, error) // ComponentVersions should return a map with a version and a list of node names that describes how many a given control-plane components there are for that version ComponentVersions(string) (map[string][]string, error) + // DNSAddonVersion returns, if deployed, the CoreDNS image tag + DNSAddonVersion() (string, error) } // KubeVersionGetter handles the version-fetching mechanism from external sources @@ -130,6 +133,11 @@ func (g *KubeVersionGetter) KubeletVersions() (map[string][]string, error) { return kubeletVersions, nil } +// DNSAddonVersion returns the CoreDNS image deployed in the cluster, or an error in case of multiple instances. +func (g *KubeVersionGetter) DNSAddonVersion() (string, error) { + return dns.DeployedDNSAddon(g.client) +} + // ComponentVersions gets the versions of the control-plane components in the cluster. // The name parameter is the name of the component to get the versions for. // The function returns a map with the version as the key and a list of node names as the value. diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index bc99a641bb66d..562c9cc87f23d 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -73,7 +73,10 @@ type Checker interface { // ContainerRuntimeCheck verifies the container runtime. type ContainerRuntimeCheck struct { - runtime utilruntime.ContainerRuntime + criSocket string + + // stubbed out for testing + impl utilruntime.Impl } // Name returns label for RuntimeCheck. @@ -84,12 +87,62 @@ func (ContainerRuntimeCheck) Name() string { // Check validates the container runtime func (crc ContainerRuntimeCheck) Check() (warnings, errorList []error) { klog.V(1).Infoln("validating the container runtime") - if err := crc.runtime.IsRunning(); err != nil { + containerRuntime := utilruntime.NewContainerRuntime(crc.criSocket) + if crc.impl != nil { + containerRuntime.SetImpl(crc.impl) + } + if err := containerRuntime.Connect(); err != nil { + return nil, []error{errors.Wrap(err, "could not connect to the container runtime")} + } + defer containerRuntime.Close() + + if err := containerRuntime.IsRunning(); err != nil { errorList = append(errorList, err) } return warnings, errorList } +// ContainerRuntimeVersionCheck verifies the version compatibility between installed container runtime and kubelet. +type ContainerRuntimeVersionCheck struct { + criSocket string + + // stubbed out for testing + impl utilruntime.Impl +} + +// Name returns label for RuntimeCheck. +func (ContainerRuntimeVersionCheck) Name() string { + return "ContainerRuntimeVersion" +} + +// Check validates the container runtime version compatibility with kubelet. +func (crvc ContainerRuntimeVersionCheck) Check() (warnings, errorList []error) { + klog.V(1).Infoln("validating the container runtime version compatibility") + containerRuntime := utilruntime.NewContainerRuntime(crvc.criSocket) + if crvc.impl != nil { + containerRuntime.SetImpl(crvc.impl) + } + if err := containerRuntime.Connect(); err != nil { + return nil, []error{errors.Wrap(err, "could not connect to the container runtime")} + } + defer containerRuntime.Close() + + ok, err := containerRuntime.IsRuntimeConfigImplemented() + if err != nil { + return nil, []error{errors.Wrap(err, "could not check if the runtime config is available")} + } + if !ok { + // TODO: return an error once the kubelet version is 1.36 or higher. + // https://github.com/kubernetes/kubeadm/issues/3229 + err := errors.New("You must update your container runtime to a version that supports the CRI method RuntimeConfig. " + + "Falling back to using cgroupDriver from kubelet config will be removed in 1.36. " + + "For more information, see https://git.k8s.io/enhancements/keps/sig-node/4033-group-driver-detection-over-cri") + warnings = append(warnings, err) + } + + return warnings, errorList +} + // ServiceCheck verifies that the given service is enabled and active. If we do not // detect a supported init system however, all checks are skipped and a warning is // returned. @@ -104,7 +157,7 @@ func (sc ServiceCheck) Name() string { if sc.Label != "" { return sc.Label } - return fmt.Sprintf("Service-%s", strings.Title(sc.Service)) + return fmt.Sprintf("Service-%s", sc.Service) } // Check validates if the service is enabled and active. @@ -483,7 +536,10 @@ func (subnet HTTPProxyCIDRCheck) Check() (warnings, errorList []error) { } // SystemVerificationCheck defines struct used for running the system verification node check in test/e2e_node/system -type SystemVerificationCheck struct{} +type SystemVerificationCheck struct { + isUpgrade bool + exec utilsexec.Interface +} // Name will return SystemVerification as name for SystemVerificationCheck func (SystemVerificationCheck) Name() string { @@ -492,7 +548,7 @@ func (SystemVerificationCheck) Name() string { // Check runs all individual checks func (sysver SystemVerificationCheck) Check() (warnings, errorList []error) { - klog.V(1).Infoln("running all checks") + klog.V(1).Infoln("running system verification checks") // Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit // Run the system verification check, but write to out buffered writer instead of stdout bufw := bufio.NewWriterSize(os.Stdout, 1*1024*1024) @@ -504,7 +560,18 @@ func (sysver SystemVerificationCheck) Check() (warnings, errorList []error) { var validators = []system.Validator{ &system.KernelValidator{Reporter: reporter}} - validators = addOSValidator(validators, reporter) + // Account for the KubeletVersion in the CgroupsValidator added + // as part of addOSValidator(). + kubeletVersion, err := GetKubeletVersion(sysver.exec) + if err != nil { + return nil, []error{errors.Wrap(err, "couldn't get kubelet version")} + } + // During upgrade we want to check the next kubelet MINOR version. + // The below approach does not support k8s MAJOR version bumps. + if sysver.isUpgrade { + kubeletVersion = kubeletVersion.WithMinor(kubeletVersion.Minor() + 1) + } + validators = addOSValidator(validators, reporter, kubeletVersion.String()) // Run all validators for _, v := range validators { @@ -661,7 +728,7 @@ func (evc ExternalEtcdVersionCheck) Check() (warnings, errorList []error) { klog.V(1).Infoln("validating the external etcd version") // Return quickly if the user isn't using external etcd - if evc.Etcd.External.Endpoints == nil { + if len(evc.Etcd.External.HTTPEndpoints) == 0 { return nil, nil } @@ -677,7 +744,7 @@ func (evc ExternalEtcdVersionCheck) Check() (warnings, errorList []error) { } client := evc.getHTTPClient(config) - for _, endpoint := range evc.Etcd.External.Endpoints { + for _, endpoint := range evc.Etcd.External.HTTPEndpoints { if _, err := url.Parse(endpoint); err != nil { errorList = append(errorList, errors.Wrapf(err, "failed to parse external etcd endpoint %s", endpoint)) continue @@ -792,11 +859,14 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{}) // ImagePullCheck will pull container images used by kubeadm type ImagePullCheck struct { - runtime utilruntime.ContainerRuntime + criSocket string imageList []string sandboxImage string imagePullPolicy v1.PullPolicy imagePullSerial bool + + // stubbed out for testing + impl utilruntime.Impl } // Name returns the label for ImagePullCheck @@ -806,6 +876,15 @@ func (ImagePullCheck) Name() string { // Check pulls images required by kubeadm. This is a mutating check func (ipc ImagePullCheck) Check() (warnings, errorList []error) { + containerRuntime := utilruntime.NewContainerRuntime(ipc.criSocket) + if ipc.impl != nil { + containerRuntime.SetImpl(ipc.impl) + } + if err := containerRuntime.Connect(); err != nil { + return nil, []error{errors.Wrap(err, "could not connect to the container runtime")} + } + defer containerRuntime.Close() + // Handle unsupported image pull policy and policy Never. policy := ipc.imagePullPolicy switch policy { @@ -820,7 +899,7 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) { } // Handle CRI sandbox image warnings. - criSandboxImage, err := ipc.runtime.SandboxImage() + criSandboxImage, err := containerRuntime.SandboxImage() if err != nil { klog.V(4).Infof("failed to detect the sandbox image for local container runtime, %v", err) } else if criSandboxImage != ipc.sandboxImage { @@ -830,7 +909,7 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) { // Perform parallel pulls. if !ipc.imagePullSerial { - if err := ipc.runtime.PullImagesInParallel(ipc.imageList, policy == v1.PullIfNotPresent); err != nil { + if err := containerRuntime.PullImagesInParallel(ipc.imageList, policy == v1.PullIfNotPresent); err != nil { errorList = append(errorList, err) } return warnings, errorList @@ -840,14 +919,14 @@ func (ipc ImagePullCheck) Check() (warnings, errorList []error) { for _, image := range ipc.imageList { switch policy { case v1.PullIfNotPresent: - if ipc.runtime.ImageExists(image) { + if containerRuntime.ImageExists(image) { klog.V(1).Infof("image exists: %s", image) continue } fallthrough // Proceed with pulling the image if it does not exist case v1.PullAlways: klog.V(1).Infof("pulling: %s", image) - if err := ipc.runtime.PullImage(image); err != nil { + if err := containerRuntime.PullImage(image); err != nil { errorList = append(errorList, errors.WithMessagef(err, "failed to pull image %s", image)) } } @@ -1041,18 +1120,14 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.JoinConfigura // addCommonChecks is a helper function to duplicate checks that are common between both the // kubeadm init and join commands func addCommonChecks(execer utilsexec.Interface, k8sVersion string, nodeReg *kubeadmapi.NodeRegistrationOptions, checks []Checker) []Checker { - containerRuntime := utilruntime.NewContainerRuntime(nodeReg.CRISocket) - if err := containerRuntime.Connect(); err != nil { - klog.Warningf("[preflight] WARNING: Couldn't create the interface used for talking to the container runtime: %v\n", err) - } else { - checks = append(checks, ContainerRuntimeCheck{runtime: containerRuntime}) - } + checks = append(checks, ContainerRuntimeCheck{criSocket: nodeReg.CRISocket}) + checks = append(checks, ContainerRuntimeVersionCheck{criSocket: nodeReg.CRISocket}) // non-windows checks checks = addSwapCheck(checks) checks = addExecChecks(checks, execer, k8sVersion) checks = append(checks, - SystemVerificationCheck{}, + SystemVerificationCheck{isUpgrade: false, exec: execer}, HostnameCheck{nodeName: nodeReg.Name}, KubeletVersionCheck{KubernetesVersion: k8sVersion, exec: execer}, ServiceCheck{Service: "kubelet", CheckIfActive: false}, @@ -1070,9 +1145,10 @@ func RunRootCheckOnly(ignorePreflightErrors sets.Set[string]) error { } // RunUpgradeChecks initializes checks slice of structs and call RunChecks -func RunUpgradeChecks(ignorePreflightErrors sets.Set[string]) error { +func RunUpgradeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfiguration, ignorePreflightErrors sets.Set[string]) error { checks := []Checker{ - SystemVerificationCheck{}, + SystemVerificationCheck{isUpgrade: true, exec: execer}, + ContainerRuntimeVersionCheck{criSocket: cfg.NodeRegistration.CRISocket}, } return RunChecks(checks, os.Stderr, ignorePreflightErrors) @@ -1080,11 +1156,6 @@ func RunUpgradeChecks(ignorePreflightErrors sets.Set[string]) error { // RunPullImagesCheck will pull images kubeadm needs if they are not found on the system func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.InitConfiguration, ignorePreflightErrors sets.Set[string]) error { - containerRuntime := utilruntime.NewContainerRuntime(cfg.NodeRegistration.CRISocket) - if err := containerRuntime.Connect(); err != nil { - return handleError(os.Stderr, err.Error()) - } - serialPull := true if cfg.NodeRegistration.ImagePullSerial != nil { serialPull = *cfg.NodeRegistration.ImagePullSerial @@ -1092,7 +1163,7 @@ func RunPullImagesCheck(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigur checks := []Checker{ ImagePullCheck{ - runtime: containerRuntime, + criSocket: cfg.NodeRegistration.CRISocket, imageList: images.GetControlPlaneImages(&cfg.ClusterConfiguration), sandboxImage: images.GetPauseImage(&cfg.ClusterConfiguration), imagePullPolicy: cfg.NodeRegistration.ImagePullPolicy, diff --git a/cmd/kubeadm/app/preflight/checks_linux.go b/cmd/kubeadm/app/preflight/checks_linux.go index 3e4bb702caf1a..e5aa7a907796b 100644 --- a/cmd/kubeadm/app/preflight/checks_linux.go +++ b/cmd/kubeadm/app/preflight/checks_linux.go @@ -46,8 +46,11 @@ func (mc MemCheck) Check() (warnings, errorList []error) { } // addOSValidator adds a new OSValidator -func addOSValidator(validators []system.Validator, reporter *system.StreamReporter) []system.Validator { - validators = append(validators, &system.OSValidator{Reporter: reporter}, &system.CgroupsValidator{Reporter: reporter}) +func addOSValidator(validators []system.Validator, reporter *system.StreamReporter, kubeletVersion string) []system.Validator { + validators = append(validators, + &system.OSValidator{Reporter: reporter}, + &system.CgroupsValidator{Reporter: reporter, KubeletVersion: kubeletVersion}, + ) return validators } diff --git a/cmd/kubeadm/app/preflight/checks_other.go b/cmd/kubeadm/app/preflight/checks_other.go index d790d7d46f39e..9756849ac8c82 100644 --- a/cmd/kubeadm/app/preflight/checks_other.go +++ b/cmd/kubeadm/app/preflight/checks_other.go @@ -25,7 +25,7 @@ import ( // addOSValidator adds a new OSValidator // No-op for Darwin (MacOS), Windows. -func addOSValidator(validators []system.Validator, _ *system.StreamReporter) []system.Validator { +func addOSValidator(validators []system.Validator, _ *system.StreamReporter, _ string) []system.Validator { return validators } diff --git a/cmd/kubeadm/app/preflight/checks_test.go b/cmd/kubeadm/app/preflight/checks_test.go index 1720677797f8b..1f61802617b42 100644 --- a/cmd/kubeadm/app/preflight/checks_test.go +++ b/cmd/kubeadm/app/preflight/checks_test.go @@ -28,6 +28,8 @@ import ( "github.com/google/go-cmp/cmp" "github.com/lithammer/dedent" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/version" @@ -39,6 +41,7 @@ import ( kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" + utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" ) var ( @@ -1021,3 +1024,84 @@ func TestJoinIPCheck(t *testing.T) { }) } } + +func TestContainerRuntimeCheck(t *testing.T) { + tests := []struct { + name string + prepare func(*utilruntime.FakeImpl) + expectErrors int + expectWarnings int + }{ + { + name: "ok", + }, + { + name: "container runtime is not running", + prepare: func(mock *utilruntime.FakeImpl) { + mock.StatusReturns(nil, errors.New("not running")) + }, + expectErrors: 1, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mock := &utilruntime.FakeImpl{} + if test.prepare != nil { + test.prepare(mock) + } + + warnings, errors := ContainerRuntimeCheck{impl: mock}.Check() + if len(warnings) != test.expectWarnings { + t.Errorf("expected %d warning(s) but got %d: %q", test.expectWarnings, len(warnings), warnings) + } + if len(errors) != test.expectErrors { + t.Errorf("expected %d error(s) but got %d: %q", test.expectErrors, len(errors), errors) + } + }) + } +} + +func TestContainerRuntimeVersionCheck(t *testing.T) { + tests := []struct { + name string + prepare func(*utilruntime.FakeImpl) + expectErrors int + expectWarnings int + }{ + { + name: "ok", + }, + { + name: "runtime config not implemented", + prepare: func(mock *utilruntime.FakeImpl) { + mock.RuntimeConfigReturns(nil, status.New(codes.Unimplemented, "not implemented").Err()) + }, + expectWarnings: 1, + }, + { + name: "call RuntimeConfig fails", + prepare: func(mock *utilruntime.FakeImpl) { + mock.RuntimeConfigReturns(nil, status.New(codes.DeadlineExceeded, "deadline exceeded").Err()) + }, + expectErrors: 1, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + mock := &utilruntime.FakeImpl{} + if test.prepare != nil { + test.prepare(mock) + } + + warnings, errors := ContainerRuntimeVersionCheck{impl: mock}.Check() + if len(warnings) != test.expectWarnings { + t.Errorf("expected %d warning(s) but got %d: %q", test.expectWarnings, len(warnings), warnings) + } + if len(errors) != test.expectErrors { + t.Errorf("expected %d error(s) but got %d: %q", test.expectErrors, len(errors), errors) + } + }) + } +} diff --git a/cmd/kubeadm/app/util/apiclient/dryrun.go b/cmd/kubeadm/app/util/apiclient/dryrun.go index 1fa69d3931c65..b52e4aa6fd2e8 100644 --- a/cmd/kubeadm/app/util/apiclient/dryrun.go +++ b/cmd/kubeadm/app/util/apiclient/dryrun.go @@ -567,9 +567,6 @@ func getNode(name string) *corev1.Node { Labels: map[string]string{ "kubernetes.io/hostname": name, }, - Annotations: map[string]string{ - constants.AnnotationKubeadmCRISocket: "dry-run-cri-socket", - }, }, } } diff --git a/cmd/kubeadm/app/util/apiclient/idempotency.go b/cmd/kubeadm/app/util/apiclient/idempotency.go index 1e97385d6767f..d02b1b9c406fe 100644 --- a/cmd/kubeadm/app/util/apiclient/idempotency.go +++ b/cmd/kubeadm/app/util/apiclient/idempotency.go @@ -192,10 +192,7 @@ func PatchNodeOnce(client clientset.Interface, nodeName string, patchFn func(*v1 if _, err := client.CoreV1().Nodes().Patch(ctx, n.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil { *lastError = errors.Wrapf(err, "error patching Node %q", n.Name) - if apierrors.IsTimeout(err) || apierrors.IsConflict(err) || apierrors.IsServerTimeout(err) || apierrors.IsServiceUnavailable(err) { - return false, nil - } - return false, *lastError + return false, nil } return true, nil diff --git a/cmd/kubeadm/app/util/apiclient/idempotency_test.go b/cmd/kubeadm/app/util/apiclient/idempotency_test.go index 02a3a2ddcb51d..a9fac6b5ab8cf 100644 --- a/cmd/kubeadm/app/util/apiclient/idempotency_test.go +++ b/cmd/kubeadm/app/util/apiclient/idempotency_test.go @@ -386,54 +386,6 @@ func TestPatchNodeOnce(t *testing.T) { }, success: false, }, - { - name: "patch node when timeout", - lookupName: "testnode", - node: v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testnode", - Labels: map[string]string{v1.LabelHostname: ""}, - }, - }, - success: false, - fakeError: apierrors.NewTimeoutError("fake timeout", -1), - }, - { - name: "patch node when conflict", - lookupName: "testnode", - node: v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testnode", - Labels: map[string]string{v1.LabelHostname: ""}, - }, - }, - success: false, - fakeError: apierrors.NewConflict(schema.GroupResource{}, "fake conflict", nil), - }, - { - name: "patch node when there is a server timeout", - lookupName: "testnode", - node: v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testnode", - Labels: map[string]string{v1.LabelHostname: ""}, - }, - }, - success: false, - fakeError: apierrors.NewServerTimeout(schema.GroupResource{}, "fake server timeout", 1), - }, - { - name: "patch node when the service is unavailable", - lookupName: "testnode", - node: v1.Node{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testnode", - Labels: map[string]string{v1.LabelHostname: ""}, - }, - }, - success: false, - fakeError: apierrors.NewServiceUnavailable("fake service unavailable"), - }, { name: "patch node failed with unknown error", lookupName: "testnode", diff --git a/cmd/kubeadm/app/util/apiclient/wait.go b/cmd/kubeadm/app/util/apiclient/wait.go index 3938f35ec34ca..e55e432f1accb 100644 --- a/cmd/kubeadm/app/util/apiclient/wait.go +++ b/cmd/kubeadm/app/util/apiclient/wait.go @@ -36,6 +36,7 @@ import ( netutil "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" @@ -279,12 +280,14 @@ func (w *KubeWaiter) WaitForControlPlaneComponents(podMap map[string]*v1.Pod, ap Get().AbsPath(comp.endpoint).Do(ctx).StatusCode(&statusCode) if err := result.Error(); err != nil { lastError = errors.WithMessagef(err, "%s check failed at %s", comp.name, url) + klog.V(5).Info(lastError) return false, nil } } else { resp, err := client.Get(url) if err != nil { lastError = errors.WithMessagef(err, "%s check failed at %s", comp.name, url) + klog.V(5).Info(lastError) return false, nil } defer func() { @@ -296,6 +299,7 @@ func (w *KubeWaiter) WaitForControlPlaneComponents(podMap map[string]*v1.Pod, ap if statusCode != http.StatusOK { lastError = errors.Errorf("%s check failed at %s with status: %d", comp.name, url, statusCode) + klog.V(5).Info(lastError) return false, nil } @@ -328,7 +332,7 @@ func (w *KubeWaiter) WaitForPodsWithLabel(kvLabel string) error { constants.KubernetesAPICallRetryInterval, w.timeout, true, func(_ context.Context) (bool, error) { listOpts := metav1.ListOptions{LabelSelector: kvLabel} - pods, err := w.client.CoreV1().Pods(metav1.NamespaceSystem).List(context.TODO(), listOpts) + pods, err := w.client.CoreV1().Pods(metav1.NamespaceSystem).List(context.Background(), listOpts) if err != nil { _, _ = fmt.Fprintf(w.writer, "[apiclient] Error getting Pods with label selector %q [%v]\n", kvLabel, err) return false, nil @@ -495,7 +499,7 @@ func (w *KubeWaiter) WaitForStaticPodHashChange(nodeName, component, previousHas func getStaticPodSingleHash(client clientset.Interface, nodeName string, component string) (string, error) { staticPodName := fmt.Sprintf("%s-%s", component, nodeName) - staticPod, err := client.CoreV1().Pods(metav1.NamespaceSystem).Get(context.TODO(), staticPodName, metav1.GetOptions{}) + staticPod, err := client.CoreV1().Pods(metav1.NamespaceSystem).Get(context.Background(), staticPodName, metav1.GetOptions{}) if err != nil { return "", errors.Wrapf(err, "failed to obtain static Pod hash for component %s on Node %s", component, nodeName) } diff --git a/cmd/kubeadm/app/util/arguments.go b/cmd/kubeadm/app/util/arguments.go index b70b9001d4dfc..6259d54b08b80 100644 --- a/cmd/kubeadm/app/util/arguments.go +++ b/cmd/kubeadm/app/util/arguments.go @@ -29,37 +29,34 @@ import ( ) // ArgumentsToCommand takes two Arg slices, one with the base arguments and one -// with optional override arguments. In the return list override arguments will precede base -// arguments. If an argument is present in the overrides, it will cause +// with optional override arguments. In the return list, base arguments will precede +// override arguments. If an argument is present in the overrides, it will cause // all instances of the same argument in the base list to be discarded, leaving // only the instances of this argument in the overrides to be applied. -func ArgumentsToCommand(base []kubeadmapi.Arg, overrides []kubeadmapi.Arg) []string { - var command []string - // Copy the overrides arguments into a new slice. - args := make([]kubeadmapi.Arg, len(overrides)) - copy(args, overrides) +func ArgumentsToCommand(base, overrides []kubeadmapi.Arg) []string { + // Sort only the base. + sortArgsSlice(&base) - // overrideArgs is a set of args which will replace the args defined in the base + // Collect all overrides in a set. overrideArgs := sets.New[string]() for _, arg := range overrides { overrideArgs.Insert(arg.Name) } + // Append only the base args that do not have overrides. + args := make([]kubeadmapi.Arg, 0, len(base)+len(overrides)) for _, arg := range base { if !overrideArgs.Has(arg.Name) { args = append(args, arg) } } - sort.Slice(args, func(i, j int) bool { - if args[i].Name == args[j].Name { - return args[i].Value < args[j].Value - } - return args[i].Name < args[j].Name - }) + // Append the overrides. + args = append(args, overrides...) - for _, arg := range args { - command = append(command, fmt.Sprintf("--%s=%s", arg.Name, arg.Value)) + command := make([]string, len(args)) + for i, arg := range args { + command[i] = fmt.Sprintf("--%s=%s", arg.Name, arg.Value) } return command @@ -85,12 +82,8 @@ func ArgumentsFromCommand(command []string) []kubeadmapi.Arg { args = append(args, kubeadmapi.Arg{Name: key, Value: val}) } - sort.Slice(args, func(i, j int) bool { - if args[i].Name == args[j].Name { - return args[i].Value < args[j].Value - } - return args[i].Name < args[j].Name - }) + sortArgsSlice(&args) + return args } @@ -117,3 +110,14 @@ func parseArgument(arg string) (string, string, error) { return keyvalSlice[0], keyvalSlice[1], nil } + +// sortArgsSlice sorts a slice of Args alpha-numerically. +func sortArgsSlice(argsPtr *[]kubeadmapi.Arg) { + args := *argsPtr + sort.Slice(args, func(i, j int) bool { + if args[i].Name == args[j].Name { + return args[i].Value < args[j].Value + } + return args[i].Name < args[j].Name + }) +} diff --git a/cmd/kubeadm/app/util/arguments_test.go b/cmd/kubeadm/app/util/arguments_test.go index a796293fb1dcf..da8b2fc5899d5 100644 --- a/cmd/kubeadm/app/util/arguments_test.go +++ b/cmd/kubeadm/app/util/arguments_test.go @@ -41,8 +41,8 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "admission-control", Value: "NamespaceLifecycle,LimitRanger"}, }, expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", "--allow-privileged=true", + "--admission-control=NamespaceLifecycle,LimitRanger", }, }, { @@ -56,9 +56,9 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "tls-sni-cert-key", Value: "/some/new/path/subpath"}, }, expected: []string{ + "--token-auth-file=/token", "--tls-sni-cert-key=/some/new/path", "--tls-sni-cert-key=/some/new/path/subpath", - "--token-auth-file=/token", }, }, { @@ -72,8 +72,8 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "tls-sni-cert-key", Value: "/some/new/path"}, }, expected: []string{ - "--tls-sni-cert-key=/some/new/path", "--token-auth-file=/token", + "--tls-sni-cert-key=/some/new/path", }, }, { @@ -85,8 +85,8 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "admission-control", Value: "NamespaceLifecycle,LimitRanger"}, }, expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", "--allow-privileged=true", + "--admission-control=NamespaceLifecycle,LimitRanger", }, }, { @@ -99,9 +99,9 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "admission-control", Value: "NamespaceLifecycle,LimitRanger"}, }, expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", "--allow-privileged=true", "--something-that-allows-empty-string=", + "--admission-control=NamespaceLifecycle,LimitRanger", }, }, { @@ -115,11 +115,31 @@ func TestArgumentsToCommand(t *testing.T) { {Name: "something-that-allows-empty-string", Value: ""}, }, expected: []string{ - "--admission-control=NamespaceLifecycle,LimitRanger", "--allow-privileged=true", + "--admission-control=NamespaceLifecycle,LimitRanger", "--something-that-allows-empty-string=", }, }, + { + name: "base are sorted and overrides are not", + base: []kubeadmapi.Arg{ + {Name: "b", Value: "true"}, + {Name: "c", Value: "true"}, + {Name: "a", Value: "true"}, + }, + overrides: []kubeadmapi.Arg{ + {Name: "e", Value: "true"}, + {Name: "b", Value: "true"}, + {Name: "d", Value: "true"}, + }, + expected: []string{ + "--a=true", + "--c=true", + "--e=true", + "--b=true", + "--d=true", + }, + }, } for _, rt := range tests { @@ -189,6 +209,21 @@ func TestArgumentsFromCommand(t *testing.T) { {Name: "tls-sni-cert-key", Value: "/some/path/subpath"}, }, }, + { + name: "args are sorted", + args: []string{ + "--c=foo", + "--a=foo", + "--b=foo", + "--b=bar", + }, + expected: []kubeadmapi.Arg{ + {Name: "a", Value: "foo"}, + {Name: "b", Value: "bar"}, + {Name: "b", Value: "foo"}, + {Name: "c", Value: "foo"}, + }, + }, } for _, rt := range tests { diff --git a/cmd/kubeadm/app/util/config/cluster.go b/cmd/kubeadm/app/util/config/cluster.go index 40ba9b2dbd188..3297c3447e55d 100644 --- a/cmd/kubeadm/app/util/config/cluster.go +++ b/cmd/kubeadm/app/util/config/cluster.go @@ -29,7 +29,6 @@ import ( authv1 "k8s.io/api/authentication/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - errorsutil "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" @@ -49,6 +48,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/util/errors" "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" "k8s.io/kubernetes/cmd/kubeadm/app/util/output" + kubeadmruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime" ) // FetchInitConfigurationFromCluster fetches configuration from a ConfigMap in the cluster @@ -116,7 +116,7 @@ func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Inte if getNodeRegistration { // gets the nodeRegistration for the current from the node object kubeconfigFile := filepath.Join(kubeconfigDir, constants.KubeletKubeConfigFileName) - if err := GetNodeRegistration(kubeconfigFile, client, &initcfg.NodeRegistration, &initcfg.ClusterConfiguration); err != nil { + if err := GetNodeRegistration(kubeconfigFile, client, &initcfg.NodeRegistration); err != nil { return nil, errors.Wrap(err, "failed to get node registration") } } @@ -163,7 +163,7 @@ func GetNodeName(kubeconfigFile string) (string, error) { } // GetNodeRegistration returns the nodeRegistration for the current node -func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, nodeRegistration *kubeadmapi.NodeRegistrationOptions, clusterCfg *kubeadmapi.ClusterConfiguration) error { +func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, nodeRegistration *kubeadmapi.NodeRegistrationOptions) error { // gets the name of the current node nodeName, err := GetNodeName(kubeconfigFile) if err != nil { @@ -181,13 +181,21 @@ func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, node configFilePath := filepath.Join(constants.KubeletRunDirectory, constants.KubeletInstanceConfigurationFileName) _, err := os.Stat(configFilePath) if os.IsNotExist(err) { - return errors.Errorf("node %s doesn't have %s annotation, or kubelet instance config %s", nodeName, constants.AnnotationKubeadmCRISocket, configFilePath) - } - kubeletConfig, err := readKubeletConfig(constants.KubeletRunDirectory, constants.KubeletInstanceConfigurationFileName) - if err != nil { - return errors.Wrapf(err, "could not read kubelet instance configuration on node %q", nodeName) + klog.Warningf("node %q lacks annotation %q and kubelet config file %q; attempting auto-detection", nodeName, constants.AnnotationKubeadmCRISocket, configFilePath) + criSocket, err = kubeadmruntime.DetectCRISocket() + if err != nil { + klog.Warningf("auto-detection of CRI socket failed for node %q: %v; falling back to default %q", nodeName, err, constants.DefaultCRISocket) + criSocket = constants.DefaultCRISocket + } + } else if err != nil { + return err + } else { + kubeletConfig, err := readKubeletConfig(constants.KubeletRunDirectory, constants.KubeletInstanceConfigurationFileName) + if err != nil { + return errors.Wrapf(err, "could not read kubelet instance configuration on node %q", nodeName) + } + criSocket = kubeletConfig.ContainerRuntimeEndpoint } - criSocket = kubeletConfig.ContainerRuntimeEndpoint } // returns the nodeRegistration attributes @@ -268,14 +276,11 @@ func GetAPIEndpoint(client clientset.Interface, nodeName string, apiEndpoint *ku func getAPIEndpointWithRetry(client clientset.Interface, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint, interval, timeout time.Duration) error { - var err error - var errs []error - - if err = getAPIEndpointFromPodAnnotation(client, nodeName, apiEndpoint, interval, timeout); err == nil { - return nil + err := getAPIEndpointFromPodAnnotation(client, nodeName, apiEndpoint, interval, timeout) + if err != nil { + return errors.WithMessagef(err, "could not retrieve API endpoints for node %q using pod annotations", nodeName) } - errs = append(errs, errors.WithMessagef(err, "could not retrieve API endpoints for node %q using pod annotations", nodeName)) - return errorsutil.NewAggregate(errs) + return nil } func getAPIEndpointFromPodAnnotation(client clientset.Interface, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint, diff --git a/cmd/kubeadm/app/util/config/cluster_test.go b/cmd/kubeadm/app/util/config/cluster_test.go index 41329eee4588b..f57770c56699c 100644 --- a/cmd/kubeadm/app/util/config/cluster_test.go +++ b/cmd/kubeadm/app/util/config/cluster_test.go @@ -330,7 +330,7 @@ func TestGetNodeRegistration(t *testing.T) { } cfg := &kubeadmapi.InitConfiguration{} - err = GetNodeRegistration(cfgPath, client, &cfg.NodeRegistration, &cfg.ClusterConfiguration) + err = GetNodeRegistration(cfgPath, client, &cfg.NodeRegistration) if rt.expectedError != (err != nil) { t.Errorf("unexpected return err from getNodeRegistration: %v", err) return diff --git a/cmd/kubeadm/app/util/etcd/etcd.go b/cmd/kubeadm/app/util/etcd/etcd.go index e5171c6ee8b93..d89e648a937b1 100644 --- a/cmd/kubeadm/app/util/etcd/etcd.go +++ b/cmd/kubeadm/app/util/etcd/etcd.go @@ -284,31 +284,42 @@ type Member struct { PeerURL string } +func (c *Client) listMembersOnce() (*clientv3.MemberListResponse, error) { + cli, err := c.newEtcdClient(c.Endpoints) + if err != nil { + return nil, err + } + defer func() { _ = cli.Close() }() + + ctx, cancel := context.WithTimeout(context.Background(), etcdTimeout) + resp, err := cli.MemberList(ctx) + cancel() + if err == nil { + return resp, nil + } + klog.V(5).Infof("Failed to get etcd member list: %v", err) + return nil, err +} + func (c *Client) listMembers(timeout time.Duration) (*clientv3.MemberListResponse, error) { // Gets the member list - var lastError error - var resp *clientv3.MemberListResponse + var ( + err error + lastError error + resp *clientv3.MemberListResponse + ) + if timeout == 0 { timeout = kubeadmapi.GetActiveTimeouts().EtcdAPICall.Duration } - err := wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, timeout, + err = wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, timeout, true, func(_ context.Context) (bool, error) { - cli, err := c.newEtcdClient(c.Endpoints) + resp, err = c.listMembersOnce() if err != nil { lastError = err - return false, nil - } - defer func() { _ = cli.Close() }() - - ctx, cancel := context.WithTimeout(context.Background(), etcdTimeout) - resp, err = cli.MemberList(ctx) - cancel() - if err == nil { - return true, nil + return false, err } - klog.V(5).Infof("Failed to get etcd member list: %v", err) - lastError = err - return false, nil + return true, nil }) if err != nil { return nil, lastError @@ -528,38 +539,74 @@ func (c *Client) addMember(name string, peerAddrs string, isLearner bool) ([]Mem return ret, nil } -// isLearner returns true if the given member ID is a learner. -func (c *Client) isLearner(memberID uint64) (bool, error) { - resp, err := c.listMembersFunc(0) +// getMemberStatus returns the status of the given member ID. +// It returns whether the member is a learner and whether it is started. +func (c *Client) getMemberStatus(memberID uint64) (isLearner bool, started bool, err error) { + resp, err := c.listMembersOnce() if err != nil { - return false, err + return false, false, err } + var m *etcdserverpb.Member for _, member := range resp.Members { - if member.ID == memberID && member.IsLearner { - return true, nil + if member.ID == memberID { + m = member + break } } - return false, nil + if m == nil { + return false, false, fmt.Errorf("member %s not found", strconv.FormatUint(memberID, 16)) + } + + started = true + // There is no field for "started". + // If the member is not started, the Name and ClientURLs fields are set to their respective zero values. + if len(m.Name) == 0 { + started = false + } + + return m.IsLearner, started, nil } // MemberPromote promotes a member as a voting member. If the given member ID is already a voting member this method -// will return early and do nothing. +// will return early and do nothing. It waits for the member to be started before attempting to promote. func (c *Client) MemberPromote(learnerID uint64) error { - isLearner, err := c.isLearner(learnerID) + var ( + lastError error + learnerIDUint = strconv.FormatUint(learnerID, 16) + ) + + klog.V(1).Infof("[etcd] Waiting for a learner to start: %s", learnerIDUint) + + err := wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, kubeadmapi.GetActiveTimeouts().EtcdAPICall.Duration, + true, func(_ context.Context) (bool, error) { + isLearner, started, err := c.getMemberStatus(learnerID) + if err != nil { + lastError = errors.WithMessagef(err, "failed to get member %s status", learnerIDUint) + return false, nil + } + if !isLearner { + klog.V(1).Infof("[etcd] Member %s was already promoted.", learnerIDUint) + return true, nil + } + if !started { + klog.V(1).Infof("[etcd] Member %s is not started yet. Waiting for it to be started.", learnerIDUint) + lastError = errors.Errorf("the etcd member %s is not started", learnerIDUint) + return false, nil + } + return true, nil + }) if err != nil { - return err - } - if !isLearner { - klog.V(1).Infof("[etcd] Member %s already promoted.", strconv.FormatUint(learnerID, 16)) - return nil + return lastError } - klog.V(1).Infof("[etcd] Promoting a learner as a voting member: %s", strconv.FormatUint(learnerID, 16)) + klog.V(1).Infof("[etcd] Promoting a learner as a voting member: %s", learnerIDUint) + cli, err := c.newEtcdClient(c.Endpoints) if err != nil { return err } + defer func() { _ = cli.Close() }() // TODO: warning logs from etcd client should be removed. @@ -568,29 +615,16 @@ func (c *Client) MemberPromote(learnerID uint64) error { // 2. context deadline exceeded // 3. peer URLs already exists // Once the client provides a way to check if the etcd learner is ready to promote, the retry logic can be revisited. - var ( - lastError error - ) err = wait.PollUntilContextTimeout(context.Background(), constants.EtcdAPICallRetryInterval, kubeadmapi.GetActiveTimeouts().EtcdAPICall.Duration, true, func(_ context.Context) (bool, error) { ctx, cancel := context.WithTimeout(context.Background(), etcdTimeout) defer cancel() - - isLearner, err := c.isLearner(learnerID) - if err != nil { - return false, err - } - if !isLearner { - klog.V(1).Infof("[etcd] Member %s was already promoted.", strconv.FormatUint(learnerID, 16)) - return true, nil - } - _, err = cli.MemberPromote(ctx, learnerID) if err == nil { - klog.V(1).Infof("[etcd] The learner was promoted as a voting member: %s", strconv.FormatUint(learnerID, 16)) + klog.V(1).Infof("[etcd] The learner was promoted as a voting member: %s", learnerIDUint) return true, nil } - klog.V(5).Infof("[etcd] Promoting the learner %s failed: %v", strconv.FormatUint(learnerID, 16), err) + klog.V(5).Infof("[etcd] Promoting the learner %s failed: %v", learnerIDUint, err) lastError = err return false, nil }) diff --git a/cmd/kubeadm/app/util/etcd/etcd_test.go b/cmd/kubeadm/app/util/etcd/etcd_test.go index 30c5436ea0d52..67671a994d29c 100644 --- a/cmd/kubeadm/app/util/etcd/etcd_test.go +++ b/cmd/kubeadm/app/util/etcd/etcd_test.go @@ -637,18 +637,19 @@ func TestListMembers(t *testing.T) { } } -func TestIsLearner(t *testing.T) { +func TestGetMemberStatus(t *testing.T) { type fields struct { Endpoints []string newEtcdClient func(endpoints []string) (etcdClient, error) listMembersFunc func(timeout time.Duration) (*clientv3.MemberListResponse, error) } tests := []struct { - name string - fields fields - memberID uint64 - want bool - wantError bool + name string + fields fields + memberID uint64 + wantLearner bool + wantStarted bool + wantError bool }{ { name: "The specified member is not a learner", @@ -670,8 +671,9 @@ func TestIsLearner(t *testing.T) { return f, nil }, }, - memberID: 1, - want: false, + memberID: 1, + wantLearner: false, + wantStarted: true, }, { name: "The specified member is a learner", @@ -700,8 +702,9 @@ func TestIsLearner(t *testing.T) { return f, nil }, }, - memberID: 1, - want: true, + memberID: 1, + wantLearner: true, + wantStarted: true, }, { name: "The specified member does not exist", @@ -714,8 +717,10 @@ func TestIsLearner(t *testing.T) { return f, nil }, }, - memberID: 3, - want: false, + memberID: 3, + wantLearner: false, + wantStarted: false, + wantError: true, }, { name: "Learner ID is empty", @@ -736,7 +741,32 @@ func TestIsLearner(t *testing.T) { return f, nil }, }, - want: true, + wantLearner: true, + wantStarted: true, + }, + { + name: "Learner member is not started (no name)", + fields: fields{ + Endpoints: []string{}, + newEtcdClient: func(endpoints []string) (etcdClient, error) { + f := &fakeEtcdClient{ + members: []*pb.Member{ + { + ID: 1, + Name: "", + PeerURLs: []string{ + "https://member2:2380", + }, + IsLearner: true, + }, + }, + } + return f, nil + }, + }, + memberID: 1, + wantLearner: true, + wantStarted: false, }, { name: "ListMembers returns an error", @@ -760,8 +790,10 @@ func TestIsLearner(t *testing.T) { return nil, errNotImplemented }, }, - want: false, - wantError: true, + memberID: 1, + wantLearner: false, + wantStarted: false, + wantError: true, }, } for _, tt := range tests { @@ -778,12 +810,15 @@ func TestIsLearner(t *testing.T) { return resp, nil } } - got, err := c.isLearner(tt.memberID) - if got != tt.want { - t.Errorf("isLearner() = %v, want %v", got, tt.want) + gotLearner, gotStarted, err := c.getMemberStatus(tt.memberID) + if gotLearner != tt.wantLearner { + t.Errorf("getMemberStatus() isLearner = %v, want %v", gotLearner, tt.wantLearner) } - if (err != nil) != (tt.wantError) { - t.Errorf("isLearner() error = %v, wantError %v", err, tt.wantError) + if gotStarted != tt.wantStarted { + t.Errorf("getMemberStatus() started = %v, want %v", gotStarted, tt.wantStarted) + } + if (err != nil) != tt.wantError { + t.Errorf("getMemberStatus() error = %v, wantError %v", err, tt.wantError) } }) } diff --git a/cmd/kubeadm/app/util/runtime/fake_impl.go b/cmd/kubeadm/app/util/runtime/fake_impl.go new file mode 100644 index 0000000000000..b3bde5a3510b2 --- /dev/null +++ b/cmd/kubeadm/app/util/runtime/fake_impl.go @@ -0,0 +1,187 @@ +/* +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 runtime + +import ( + "context" + "time" + + cri "k8s.io/cri-api/pkg/apis" + v1 "k8s.io/cri-api/pkg/apis/runtime/v1" +) + +// FakeImpl is a fake implementation of the impl interface. +type FakeImpl struct { + runtimeConfigReturns struct { + res *v1.RuntimeConfigResponse + err error + } + imageStatusReturns struct { + res *v1.ImageStatusResponse + err error + } + listPodSandboxReturns struct { + res []*v1.PodSandbox + err error + } + newRemoteImageServiceReturns struct { + res cri.ImageManagerService + err error + } + newRemoteRuntimeServiceReturns struct { + res cri.RuntimeService + err error + } + pullImageReturns struct { + res string + err error + } + removePodSandboxReturns struct { + res error + } + statusReturns struct { + res *v1.StatusResponse + err error + } + stopPodSandboxReturns struct { + res error + } +} + +// ImageStatus returns the status of the image. +func (fake *FakeImpl) ImageStatus(context.Context, cri.ImageManagerService, *v1.ImageSpec, bool) (*v1.ImageStatusResponse, error) { + fakeReturns := fake.imageStatusReturns + return fakeReturns.res, fakeReturns.err +} + +// ImageStatusReturns sets the return values for the ImageStatus method. +func (fake *FakeImpl) ImageStatusReturns(res *v1.ImageStatusResponse, err error) { + fake.imageStatusReturns = struct { + res *v1.ImageStatusResponse + err error + }{res, err} +} + +// ListPodSandbox returns the list of pod sandboxes. +func (fake *FakeImpl) ListPodSandbox(context.Context, cri.RuntimeService, *v1.PodSandboxFilter) ([]*v1.PodSandbox, error) { + fakeReturns := fake.listPodSandboxReturns + return fakeReturns.res, fakeReturns.err +} + +// ListPodSandboxReturns sets the return values for the ListPodSandbox method. +func (fake *FakeImpl) ListPodSandboxReturns(res []*v1.PodSandbox, err error) { + fake.listPodSandboxReturns = struct { + res []*v1.PodSandbox + err error + }{res, err} +} + +// NewRemoteImageService returns the new remote image service. +func (fake *FakeImpl) NewRemoteImageService(string, time.Duration) (cri.ImageManagerService, error) { + fakeReturns := fake.newRemoteImageServiceReturns + return fakeReturns.res, fakeReturns.err +} + +// NewRemoteImageServiceReturns sets the return values for the NewRemoteImageService method. +func (fake *FakeImpl) NewRemoteImageServiceReturns(res cri.ImageManagerService, err error) { + fake.newRemoteImageServiceReturns = struct { + res cri.ImageManagerService + err error + }{res, err} +} + +// NewRemoteRuntimeService returns the new remote runtime service. +func (fake *FakeImpl) NewRemoteRuntimeService(string, time.Duration) (cri.RuntimeService, error) { + fakeReturns := fake.newRemoteRuntimeServiceReturns + return fakeReturns.res, fakeReturns.err +} + +// NewRemoteRuntimeServiceReturns sets the return values for the NewRemoteRuntimeService method. +func (fake *FakeImpl) NewRemoteRuntimeServiceReturns(res cri.RuntimeService, err error) { + fake.newRemoteRuntimeServiceReturns = struct { + res cri.RuntimeService + err error + }{res, err} +} + +// PullImage returns the pull image. +func (fake *FakeImpl) PullImage(context.Context, cri.ImageManagerService, *v1.ImageSpec, *v1.AuthConfig, *v1.PodSandboxConfig) (string, error) { + fakeReturns := fake.pullImageReturns + return fakeReturns.res, fakeReturns.err +} + +// PullImageReturns sets the return values for the PullImage method. +func (fake *FakeImpl) PullImageReturns(res string, err error) { + fake.pullImageReturns = struct { + res string + err error + }{res, err} +} + +// RemovePodSandbox removes the pod sandbox. +func (fake *FakeImpl) RemovePodSandbox(context.Context, cri.RuntimeService, string) error { + fakeReturns := fake.removePodSandboxReturns + return fakeReturns.res +} + +// RemovePodSandboxReturns sets the return values for the RemovePodSandbox method. +func (fake *FakeImpl) RemovePodSandboxReturns(res error) { + fake.removePodSandboxReturns = struct { + res error + }{res} +} + +// RuntimeConfig returns the runtime config. +func (fake *FakeImpl) RuntimeConfig(context.Context, cri.RuntimeService) (*v1.RuntimeConfigResponse, error) { + fakeReturns := fake.runtimeConfigReturns + return fakeReturns.res, fakeReturns.err +} + +// RuntimeConfigReturns sets the return values for the RuntimeConfig method. +func (fake *FakeImpl) RuntimeConfigReturns(res *v1.RuntimeConfigResponse, err error) { + fake.runtimeConfigReturns = struct { + res *v1.RuntimeConfigResponse + err error + }{res, err} +} + +// Status returns the status of the runtime. +func (fake *FakeImpl) Status(context.Context, cri.RuntimeService, bool) (*v1.StatusResponse, error) { + fakeReturns := fake.statusReturns + return fakeReturns.res, fakeReturns.err +} + +// StatusReturns sets the return values for the Status method. +func (fake *FakeImpl) StatusReturns(res *v1.StatusResponse, err error) { + fake.statusReturns = struct { + res *v1.StatusResponse + err error + }{res, err} +} + +// StopPodSandbox stops the pod sandbox. +func (fake *FakeImpl) StopPodSandbox(context.Context, cri.RuntimeService, string) error { + fakeReturns := fake.stopPodSandboxReturns + return fakeReturns.res +} + +// StopPodSandboxReturns sets the return values for the StopPodSandbox method. +func (fake *FakeImpl) StopPodSandboxReturns(res error) { + fake.stopPodSandboxReturns = struct { + res error + }{res} +} diff --git a/cmd/kubeadm/app/util/runtime/impl.go b/cmd/kubeadm/app/util/runtime/impl.go index b71f589d45c29..e44f56b382416 100644 --- a/cmd/kubeadm/app/util/runtime/impl.go +++ b/cmd/kubeadm/app/util/runtime/impl.go @@ -27,9 +27,11 @@ import ( type defaultImpl struct{} -type impl interface { +// Impl is an interface for the container runtime implementation. +type Impl interface { NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration) (criapi.RuntimeService, error) NewRemoteImageService(endpoint string, connectionTimeout time.Duration) (criapi.ImageManagerService, error) + RuntimeConfig(ctx context.Context, runtimeService criapi.RuntimeService) (*runtimeapi.RuntimeConfigResponse, error) Status(ctx context.Context, runtimeService criapi.RuntimeService, verbose bool) (*runtimeapi.StatusResponse, error) ListPodSandbox(ctx context.Context, runtimeService criapi.RuntimeService, filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) StopPodSandbox(ctx context.Context, runtimeService criapi.RuntimeService, sandboxID string) error @@ -46,6 +48,10 @@ func (*defaultImpl) NewRemoteImageService(endpoint string, connectionTimeout tim return criclient.NewRemoteImageService(endpoint, connectionTimeout, nil, nil) } +func (*defaultImpl) RuntimeConfig(ctx context.Context, runtimeService criapi.RuntimeService) (*runtimeapi.RuntimeConfigResponse, error) { + return runtimeService.RuntimeConfig(ctx) +} + func (*defaultImpl) Status(ctx context.Context, runtimeService criapi.RuntimeService, verbose bool) (*runtimeapi.StatusResponse, error) { return runtimeService.Status(ctx, verbose) } diff --git a/cmd/kubeadm/app/util/runtime/runtime.go b/cmd/kubeadm/app/util/runtime/runtime.go index 851af6f844a28..831d1478e8cd7 100644 --- a/cmd/kubeadm/app/util/runtime/runtime.go +++ b/cmd/kubeadm/app/util/runtime/runtime.go @@ -23,6 +23,9 @@ import ( "strings" "time" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + errorsutil "k8s.io/apimachinery/pkg/util/errors" criapi "k8s.io/cri-api/pkg/apis" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -42,7 +45,8 @@ var defaultKnownCRISockets = []string{ // ContainerRuntime is an interface for working with container runtimes type ContainerRuntime interface { Connect() error - SetImpl(impl) + Close() + SetImpl(Impl) IsRunning() error ListKubeContainers() ([]string, error) RemoveContainers(containers []string) error @@ -50,11 +54,12 @@ type ContainerRuntime interface { PullImagesInParallel(images []string, ifNotPresent bool) error ImageExists(image string) bool SandboxImage() (string, error) + IsRuntimeConfigImplemented() (bool, error) } // CRIRuntime is a struct that interfaces with the CRI type CRIRuntime struct { - impl impl + impl Impl criSocket string runtimeService criapi.RuntimeService imageService criapi.ImageManagerService @@ -72,7 +77,7 @@ func NewContainerRuntime(criSocket string) ContainerRuntime { } // SetImpl can be used to set the internal implementation for testing purposes. -func (runtime *CRIRuntime) SetImpl(impl impl) { +func (runtime *CRIRuntime) SetImpl(impl Impl) { runtime.impl = impl } @@ -93,6 +98,20 @@ func (runtime *CRIRuntime) Connect() error { return nil } +// Close closes the connections to the runtime and image services. +func (runtime *CRIRuntime) Close() { + if runtime.runtimeService != nil { + if err := runtime.runtimeService.Close(); err != nil { + klog.Warningf("failed to close runtime service: %v", err) + } + } + if runtime.imageService != nil { + if err := runtime.imageService.Close(); err != nil { + klog.Warningf("failed to close image service: %v", err) + } + } +} + // IsRunning checks if runtime is running. func (runtime *CRIRuntime) IsRunning() error { ctx, cancel := defaultContext() @@ -299,3 +318,18 @@ func (runtime *CRIRuntime) SandboxImage() (string, error) { return c.SandboxImage, nil } + +// IsRuntimeConfigImplemented checks if the container runtime supports the RuntimeConfig gRPC method +func (runtime *CRIRuntime) IsRuntimeConfigImplemented() (bool, error) { + ctx, cancel := defaultContext() + defer cancel() + _, err := runtime.impl.RuntimeConfig(ctx, runtime.runtimeService) + if err != nil { + s, ok := status.FromError(err) + if !ok || s.Code() != codes.Unimplemented { + return false, errors.Wrap(err, "failed to call RuntimeConfig gRPC method") + } + return false, nil + } + return true, nil +} diff --git a/cmd/kubeadm/app/util/runtime/runtime_fake_test.go b/cmd/kubeadm/app/util/runtime/runtime_fake_test.go deleted file mode 100644 index e42e239e575f0..0000000000000 --- a/cmd/kubeadm/app/util/runtime/runtime_fake_test.go +++ /dev/null @@ -1,152 +0,0 @@ -/* -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 runtime - -import ( - "context" - "time" - - cri "k8s.io/cri-api/pkg/apis" - v1 "k8s.io/cri-api/pkg/apis/runtime/v1" -) - -type fakeImpl struct { - imageStatusReturns struct { - res *v1.ImageStatusResponse - err error - } - listPodSandboxReturns struct { - res []*v1.PodSandbox - err error - } - newRemoteImageServiceReturns struct { - res cri.ImageManagerService - err error - } - newRemoteRuntimeServiceReturns struct { - res cri.RuntimeService - err error - } - pullImageReturns struct { - res string - err error - } - removePodSandboxReturns struct { - res error - } - statusReturns struct { - res *v1.StatusResponse - err error - } - stopPodSandboxReturns struct { - res error - } -} - -func (fake *fakeImpl) ImageStatus(context.Context, cri.ImageManagerService, *v1.ImageSpec, bool) (*v1.ImageStatusResponse, error) { - fakeReturns := fake.imageStatusReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) ImageStatusReturns(res *v1.ImageStatusResponse, err error) { - fake.imageStatusReturns = struct { - res *v1.ImageStatusResponse - err error - }{res, err} -} - -func (fake *fakeImpl) ListPodSandbox(context.Context, cri.RuntimeService, *v1.PodSandboxFilter) ([]*v1.PodSandbox, error) { - fakeReturns := fake.listPodSandboxReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) ListPodSandboxReturns(res []*v1.PodSandbox, err error) { - fake.listPodSandboxReturns = struct { - res []*v1.PodSandbox - err error - }{res, err} -} - -func (fake *fakeImpl) NewRemoteImageService(string, time.Duration) (cri.ImageManagerService, error) { - fakeReturns := fake.newRemoteImageServiceReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) NewRemoteImageServiceReturns(res cri.ImageManagerService, err error) { - fake.newRemoteImageServiceReturns = struct { - res cri.ImageManagerService - err error - }{res, err} -} - -func (fake *fakeImpl) NewRemoteRuntimeService(string, time.Duration) (cri.RuntimeService, error) { - fakeReturns := fake.newRemoteRuntimeServiceReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) NewRemoteRuntimeServiceReturns(res cri.RuntimeService, err error) { - fake.newRemoteRuntimeServiceReturns = struct { - res cri.RuntimeService - err error - }{res, err} -} - -func (fake *fakeImpl) PullImage(context.Context, cri.ImageManagerService, *v1.ImageSpec, *v1.AuthConfig, *v1.PodSandboxConfig) (string, error) { - fakeReturns := fake.pullImageReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) PullImageReturns(res string, err error) { - fake.pullImageReturns = struct { - res string - err error - }{res, err} -} - -func (fake *fakeImpl) RemovePodSandbox(context.Context, cri.RuntimeService, string) error { - fakeReturns := fake.removePodSandboxReturns - return fakeReturns.res -} - -func (fake *fakeImpl) RemovePodSandboxReturns(res error) { - fake.removePodSandboxReturns = struct { - res error - }{res} -} - -func (fake *fakeImpl) Status(context.Context, cri.RuntimeService, bool) (*v1.StatusResponse, error) { - fakeReturns := fake.statusReturns - return fakeReturns.res, fakeReturns.err -} - -func (fake *fakeImpl) StatusReturns(res *v1.StatusResponse, err error) { - fake.statusReturns = struct { - res *v1.StatusResponse - err error - }{res, err} -} - -func (fake *fakeImpl) StopPodSandbox(context.Context, cri.RuntimeService, string) error { - fakeReturns := fake.stopPodSandboxReturns - return fakeReturns.res -} - -func (fake *fakeImpl) StopPodSandboxReturns(res error) { - fake.stopPodSandboxReturns = struct { - res error - }{res} -} diff --git a/cmd/kubeadm/app/util/runtime/runtime_test.go b/cmd/kubeadm/app/util/runtime/runtime_test.go index b1048dc792d5a..7b6d60ebe8bc6 100644 --- a/cmd/kubeadm/app/util/runtime/runtime_test.go +++ b/cmd/kubeadm/app/util/runtime/runtime_test.go @@ -24,6 +24,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" v1 "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -32,11 +34,12 @@ import ( ) var errTest = errors.New("test") +var errNotImplemented = status.New(codes.Unimplemented, "not implemented").Err() func TestNewContainerRuntime(t *testing.T) { for _, tc := range []struct { name string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { @@ -45,14 +48,14 @@ func TestNewContainerRuntime(t *testing.T) { }, { name: "invalid: new runtime service fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.NewRemoteRuntimeServiceReturns(nil, errTest) }, shouldError: true, }, { name: "invalid: new image service fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.NewRemoteImageServiceReturns(nil, errTest) }, shouldError: true, @@ -60,7 +63,7 @@ func TestNewContainerRuntime(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -76,7 +79,7 @@ func TestNewContainerRuntime(t *testing.T) { func TestIsRunning(t *testing.T) { for _, tc := range []struct { name string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { @@ -85,14 +88,14 @@ func TestIsRunning(t *testing.T) { }, { name: "invalid: runtime status fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(nil, errTest) }, shouldError: true, }, { name: "invalid: runtime condition status not 'true'", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(&v1.StatusResponse{Status: &v1.RuntimeStatus{ Conditions: []*v1.RuntimeCondition{ { @@ -107,7 +110,7 @@ func TestIsRunning(t *testing.T) { }, { name: "valid: runtime condition type does not match", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(&v1.StatusResponse{Status: &v1.RuntimeStatus{ Conditions: []*v1.RuntimeCondition{ { @@ -123,7 +126,7 @@ func TestIsRunning(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -140,12 +143,12 @@ func TestListKubeContainers(t *testing.T) { for _, tc := range []struct { name string expected []string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { name: "valid", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.ListPodSandboxReturns([]*v1.PodSandbox{ {Id: "first"}, {Id: "second"}, @@ -156,7 +159,7 @@ func TestListKubeContainers(t *testing.T) { }, { name: "invalid: list pod sandbox fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.ListPodSandboxReturns(nil, errTest) }, shouldError: true, @@ -164,7 +167,7 @@ func TestListKubeContainers(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -181,12 +184,12 @@ func TestListKubeContainers(t *testing.T) { func TestSandboxImage(t *testing.T) { for _, tc := range []struct { name, expected string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { name: "valid", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{ "config": `{"sandboxImage": "pause"}`, }}, nil) @@ -196,14 +199,14 @@ func TestSandboxImage(t *testing.T) { }, { name: "invalid: runtime status fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(nil, errTest) }, shouldError: true, }, { name: "invalid: no config JSON", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{ "config": "wrong", }}, nil) @@ -212,7 +215,7 @@ func TestSandboxImage(t *testing.T) { }, { name: "invalid: no config", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StatusReturns(&v1.StatusResponse{Info: map[string]string{}}, nil) }, shouldError: true, @@ -220,7 +223,7 @@ func TestSandboxImage(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -238,7 +241,7 @@ func TestRemoveContainers(t *testing.T) { for _, tc := range []struct { name string containers []string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { @@ -252,7 +255,7 @@ func TestRemoveContainers(t *testing.T) { { name: "invalid: remove pod sandbox fails", containers: []string{"1"}, - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.RemovePodSandboxReturns(errTest) }, shouldError: true, @@ -260,7 +263,7 @@ func TestRemoveContainers(t *testing.T) { { name: "invalid: stop pod sandbox fails", containers: []string{"1"}, - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.StopPodSandboxReturns(errTest) }, shouldError: true, @@ -268,7 +271,7 @@ func TestRemoveContainers(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -284,7 +287,7 @@ func TestRemoveContainers(t *testing.T) { func TestPullImage(t *testing.T) { for _, tc := range []struct { name string - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { @@ -292,7 +295,7 @@ func TestPullImage(t *testing.T) { }, { name: "invalid: pull image fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.PullImageReturns("", errTest) }, shouldError: true, @@ -300,7 +303,7 @@ func TestPullImage(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -317,11 +320,11 @@ func TestImageExists(t *testing.T) { for _, tc := range []struct { name string expected bool - prepare func(*fakeImpl) + prepare func(*FakeImpl) }{ { name: "valid", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.ImageStatusReturns(&v1.ImageStatusResponse{ Image: &v1.Image{}, }, nil) @@ -330,7 +333,7 @@ func TestImageExists(t *testing.T) { }, { name: "invalid: image status fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.ImageStatusReturns(nil, errTest) }, expected: false, @@ -338,7 +341,7 @@ func TestImageExists(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -466,7 +469,7 @@ func TestPullImagesInParallel(t *testing.T) { for _, tc := range []struct { name string ifNotPresent bool - prepare func(*fakeImpl) + prepare func(*FakeImpl) shouldError bool }{ { @@ -478,7 +481,7 @@ func TestPullImagesInParallel(t *testing.T) { }, { name: "invalid: pull fails", - prepare: func(mock *fakeImpl) { + prepare: func(mock *FakeImpl) { mock.PullImageReturns("", errTest) }, shouldError: true, @@ -486,7 +489,7 @@ func TestPullImagesInParallel(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { containerRuntime := NewContainerRuntime("") - mock := &fakeImpl{} + mock := &FakeImpl{} if tc.prepare != nil { tc.prepare(mock) } @@ -498,3 +501,51 @@ func TestPullImagesInParallel(t *testing.T) { }) } } + +func TestIsRuntimeConfigEnabled(t *testing.T) { + for _, tc := range []struct { + name string + prepare func(*FakeImpl) + shouldError bool + expected bool + }{ + { + name: "valid: RuntimeConfig gRPC method is implemented", + prepare: func(mock *FakeImpl) { + mock.RuntimeConfigReturns(&v1.RuntimeConfigResponse{}, nil) + }, + shouldError: false, + expected: true, + }, + { + name: "invalid: RuntimeConfig gRPC method is not implemented", + prepare: func(mock *FakeImpl) { + mock.RuntimeConfigReturns(nil, errNotImplemented) + }, + shouldError: false, + expected: false, + }, + { + name: "invalid: RuntimeConfig gRPC method returns an error", + prepare: func(mock *FakeImpl) { + mock.RuntimeConfigReturns(nil, errTest) + }, + shouldError: true, + expected: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + containerRuntime := NewContainerRuntime("") + mock := &FakeImpl{} + if tc.prepare != nil { + tc.prepare(mock) + } + containerRuntime.SetImpl(mock) + + enabled, err := containerRuntime.IsRuntimeConfigImplemented() + + assert.Equal(t, tc.shouldError, err != nil) + assert.Equal(t, tc.expected, enabled) + }) + } +} diff --git a/cmd/kubeadm/app/util/staticpod/utils.go b/cmd/kubeadm/app/util/staticpod/utils.go index b49940d6c9d09..c5110eb94c6ba 100644 --- a/cmd/kubeadm/app/util/staticpod/utils.go +++ b/cmd/kubeadm/app/util/staticpod/utils.go @@ -18,9 +18,9 @@ package staticpod import ( "bytes" - "crypto/md5" "fmt" "hash" + "hash/fnv" "io" "math" "net/url" @@ -373,7 +373,7 @@ func ManifestFilesAreEqual(path1, path2 string) (bool, string, error) { return false, "", err } - hasher := md5.New() + hasher := fnv.New128a() DeepHashObject(hasher, pod1) hash1 := hasher.Sum(nil)[0:] DeepHashObject(hasher, pod2) diff --git a/cmd/kubeadm/app/util/staticpod/utils_linux.go b/cmd/kubeadm/app/util/staticpod/utils_linux.go index cdd371f1e7402..27003c46f37b4 100644 --- a/cmd/kubeadm/app/util/staticpod/utils_linux.go +++ b/cmd/kubeadm/app/util/staticpod/utils_linux.go @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package staticpod contains utilities for managing the static pod. package staticpod import ( diff --git a/cmd/kubeadm/app/util/staticpod/utils_others.go b/cmd/kubeadm/app/util/staticpod/utils_others.go index c259f48b3ecc0..59e520d385679 100644 --- a/cmd/kubeadm/app/util/staticpod/utils_others.go +++ b/cmd/kubeadm/app/util/staticpod/utils_others.go @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package staticpod contains utilities for managing the static pod. package staticpod import ( diff --git a/cmd/kubeadm/app/util/users/users_linux.go b/cmd/kubeadm/app/util/users/users_linux.go index 09308f98d83db..8c70bffbd9b2e 100644 --- a/cmd/kubeadm/app/util/users/users_linux.go +++ b/cmd/kubeadm/app/util/users/users_linux.go @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package users contains utilities for managing the users. package users import ( @@ -238,12 +239,17 @@ func addUsersAndGroupsImpl(pathLoginDef, pathUsers, pathGroups string) (*UsersAn } // Prepare the maps of users and groups. - usersConcat := append(users, usersToCreate...) + var usersConcat []*entry + usersConcat = append(usersConcat, users...) + usersConcat = append(usersConcat, usersToCreate...) mapUsers, err := entriesToEntryMap(usersConcat, usersToCreateSpec) if err != nil { return nil, err } - groupsConcat := append(groups, groupsToCreate...) + + var groupsConcat []*entry + groupsConcat = append(groupsConcat, groups...) + groupsConcat = append(groupsConcat, groupsToCreate...) mapGroups, err := entriesToEntryMap(groupsConcat, groupsToCreateSpec) if err != nil { return nil, err @@ -593,15 +599,15 @@ func openFileWithLock(path string) (f *os.File, close func(), err error) { } } if err != nil { - f.Close() + _ = f.Close() return nil, nil, err } close = func() { // This function should be called once operations with the file are finished. // It unlocks the file and closes it. unlock := syscall.Flock_t{Type: syscall.F_UNLCK} - syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &unlock) - f.Close() + _ = syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &unlock) + _ = f.Close() } return f, close, nil } diff --git a/cmd/kubeadm/app/util/users/users_linux_test.go b/cmd/kubeadm/app/util/users/users_linux_test.go index e0c1368ba0527..1baee04ceb990 100644 --- a/cmd/kubeadm/app/util/users/users_linux_test.go +++ b/cmd/kubeadm/app/util/users/users_linux_test.go @@ -579,7 +579,7 @@ func writeTempFile(t *testing.T, contents string) (string, func()) { t.Fatalf("could not write file: %v", err) } close := func() { - os.Remove(file.Name()) + _ = os.Remove(file.Name()) } return file.Name(), close } diff --git a/cmd/kubeadm/app/util/users/users_other.go b/cmd/kubeadm/app/util/users/users_other.go index ba79ebfbb069a..6c2b51bda1f16 100644 --- a/cmd/kubeadm/app/util/users/users_other.go +++ b/cmd/kubeadm/app/util/users/users_other.go @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Package users contains utilities for managing the users. package users // EntryMap is empty on non-Linux. diff --git a/cmd/kubelet/app/auth_test.go b/cmd/kubelet/app/auth_test.go new file mode 100644 index 0000000000000..06498486b9c38 --- /dev/null +++ b/cmd/kubelet/app/auth_test.go @@ -0,0 +1,208 @@ +/* +Copyright 2025 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 app + +import ( + "bytes" + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + + "k8s.io/apiserver/pkg/authorization/authorizer" + authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1" + authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1" + "k8s.io/client-go/rest" + kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" +) + +func TestAuthzWebhookRequestEncoding(t *testing.T) { + testCases := []struct { + name string + ContentType string + ExpectContentType string + ExpectRequestBodyPrefix []byte + }{ + { + name: "json", + ContentType: "application/json", + ExpectContentType: "application/json", + ExpectRequestBodyPrefix: []byte(`{`), + }, + { + name: "empty", + ContentType: "", + ExpectContentType: "application/json", + ExpectRequestBodyPrefix: []byte(`{`), + }, + { + name: "protobuf", + ContentType: "application/vnd.kubernetes.protobuf", + ExpectContentType: "application/vnd.kubernetes.protobuf", + ExpectRequestBodyPrefix: []byte("\x6b\x38\x73\x00"), // k8s protobuf magic number + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + handlerInvoked := make(chan struct{}) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer close(handlerInvoked) + + if got := r.Header.Get("Content-Type"); got != tc.ExpectContentType { + t.Errorf("unexpected Content-Type: got %q, want %q", got, tc.ExpectContentType) + } + + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("failed to read request body: %v", err) + } + if !bytes.HasPrefix(body, tc.ExpectRequestBodyPrefix) { + t.Errorf("request body should have prefix %q, but got %q", tc.ExpectRequestBodyPrefix, body) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + if _, err := w.Write([]byte(`{"kind":"SubjectAccessReview","apiVersion":"authorization.k8s.io/v1","status":{"allowed":true}}`)); err != nil { + t.Fatalf("unexpected response write failure: %v", err) + } + })) + defer server.Close() + + cfg := &rest.Config{ + Host: server.URL, + ContentConfig: rest.ContentConfig{ + ContentType: tc.ContentType, + }, + } + + authzClient, err := authorizationv1client.NewForConfigAndClient(cfg, server.Client()) + if err != nil { + t.Fatalf("failed to create authorization client: %v", err) + } + + authz, err := BuildAuthz(authzClient, kubeletconfig.KubeletAuthorization{Mode: kubeletconfig.KubeletAuthorizationModeWebhook}) + if err != nil { + t.Fatalf("failed to build authorizer: %v", err) + } + + if _, _, err := authz.Authorize(context.Background(), &authorizer.AttributesRecord{}); err != nil { + t.Fatalf("Authorize failed: %v", err) + } + + select { + case <-handlerInvoked: + default: + t.Fatal("webhook handler not invoked") + } + }) + } +} + +func TestAuthnWebhookRequestEncoding(t *testing.T) { + testCases := []struct { + name string + ContentType string + ExpectContentType string + ExpectRequestBodyPrefix []byte + }{ + { + name: "json", + ContentType: "application/json", + ExpectContentType: "application/json", + ExpectRequestBodyPrefix: []byte(`{`), + }, + { + name: "empty", + ContentType: "", + ExpectContentType: "application/json", + ExpectRequestBodyPrefix: []byte(`{`), + }, + { + name: "protobuf", + ContentType: "application/vnd.kubernetes.protobuf", + ExpectContentType: "application/vnd.kubernetes.protobuf", + ExpectRequestBodyPrefix: []byte("\x6b\x38\x73\x00"), // k8s protobuf magic number + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + handlerInvoked := make(chan struct{}) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer close(handlerInvoked) + + if got := r.Header.Get("Content-Type"); got != tc.ExpectContentType { + t.Errorf("unexpected Content-Type: got %q, want %q", got, tc.ExpectContentType) + } + + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("failed to read request body: %v", err) + } + if !bytes.HasPrefix(body, tc.ExpectRequestBodyPrefix) { + t.Errorf("request body should have prefix %q, but got %q", tc.ExpectRequestBodyPrefix, body) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + if _, err := w.Write([]byte(`{"kind":"TokenReview","apiVersion":"authentication.k8s.io/v1","status":{"authenticated":true}}`)); err != nil { + t.Fatalf("unexpected response write failure: %v", err) + } + })) + defer server.Close() + + cfg := &rest.Config{ + Host: server.URL, + ContentConfig: rest.ContentConfig{ + ContentType: tc.ContentType, + }, + } + + authnClient, err := authenticationv1client.NewForConfigAndClient(cfg, server.Client()) + if err != nil { + t.Fatalf("failed to create authentication client: %v", err) + } + + authn, _, err := BuildAuthn(authnClient, kubeletconfig.KubeletAuthentication{ + Webhook: kubeletconfig.KubeletWebhookAuthentication{ + Enabled: true, + }, + }) + if err != nil { + t.Fatalf("failed to build authenticator: %v", err) + } + + request, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, "/fooz", nil) + if err != nil { + t.Fatalf("failed to build test request: %v", err) + } + request.Header.Set("Authorization", "Bearer foo") + + if _, _, err := authn.AuthenticateRequest(request); err != nil { + t.Fatalf("AuthenticateToken failed: %v", err) + } + + select { + case <-handlerInvoked: + default: + t.Fatal("webhook handler not invoked") + } + }) + } +} diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index 2cd3b1639193e..7b6d6f7d2c047 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -34,10 +34,10 @@ import ( "k8s.io/kubelet/config/v1beta1" kubeletapis "k8s.io/kubelet/pkg/apis" "k8s.io/kubernetes/pkg/cluster/ports" - kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" + kubeletconfigapi "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/config/scheme" kubeletconfigvalidation "k8s.io/kubernetes/pkg/kubelet/apis/config/validation" - "k8s.io/kubernetes/pkg/kubelet/config" + "k8s.io/kubernetes/pkg/kubelet/kubeletconfig" utilflag "k8s.io/kubernetes/pkg/util/flag" ) @@ -63,7 +63,7 @@ type KubeletFlags struct { NodeIP string // Container-runtime-specific options. - config.ContainerRuntimeOptions + kubeletconfig.ContainerRuntimeOptions // certDirectory is the directory where the TLS certs are located. // If tlsCertFile and tlsPrivateKeyFile are provided, this flag will be ignored. @@ -134,7 +134,7 @@ type KubeletFlags struct { // NewKubeletFlags will create a new KubeletFlags with default values func NewKubeletFlags() *KubeletFlags { return &KubeletFlags{ - ContainerRuntimeOptions: config.ContainerRuntimeOptions{}, + ContainerRuntimeOptions: kubeletconfig.ContainerRuntimeOptions{}, CertDirectory: "/var/lib/kubelet/pki", RootDirectory: filepath.Clean(defaultRootDir), MaxContainerCount: -1, @@ -195,14 +195,14 @@ func getLabelNamespace(key string) string { } // NewKubeletConfiguration will create a new KubeletConfiguration with default values -func NewKubeletConfiguration() (*kubeletconfig.KubeletConfiguration, error) { +func NewKubeletConfiguration() (*kubeletconfigapi.KubeletConfiguration, error) { scheme, _, err := kubeletscheme.NewSchemeAndCodecs() if err != nil { return nil, err } versioned := &v1beta1.KubeletConfiguration{} scheme.Default(versioned) - config := &kubeletconfig.KubeletConfiguration{} + config := &kubeletconfigapi.KubeletConfiguration{} if err := scheme.Convert(versioned, config, nil); err != nil { return nil, err } @@ -213,13 +213,13 @@ func NewKubeletConfiguration() (*kubeletconfig.KubeletConfiguration, error) { // applyLegacyDefaults applies legacy default values to the KubeletConfiguration in order to // preserve the command line API. This is used to construct the baseline default KubeletConfiguration // before the first round of flag parsing. -func applyLegacyDefaults(kc *kubeletconfig.KubeletConfiguration) { +func applyLegacyDefaults(kc *kubeletconfigapi.KubeletConfiguration) { // --anonymous-auth kc.Authentication.Anonymous.Enabled = true // --authentication-token-webhook kc.Authentication.Webhook.Enabled = false // --authorization-mode - kc.Authorization.Mode = kubeletconfig.KubeletAuthorizationModeAlwaysAllow + kc.Authorization.Mode = kubeletconfigapi.KubeletAuthorizationModeAlwaysAllow // --read-only-port kc.ReadOnlyPort = ports.KubeletReadOnlyPort } @@ -228,7 +228,7 @@ func applyLegacyDefaults(kc *kubeletconfig.KubeletConfiguration) { // a kubelet. These can either be set via command line or directly. type KubeletServer struct { KubeletFlags - kubeletconfig.KubeletConfiguration + kubeletconfigapi.KubeletConfiguration } // NewKubeletServer will create a new KubeletServer with default values. @@ -276,7 +276,7 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) { mainfs.AddFlagSet(fs) }() - f.ContainerRuntimeOptions.AddFlags(fs) + addContainerRuntimeFlags(fs, &f.ContainerRuntimeOptions) f.addOSFlags(fs) fs.StringVar(&f.KubeletConfigFile, "config", f.KubeletConfigFile, "The Kubelet will load its initial configuration from this file. The path may be absolute or relative; relative paths start at the Kubelet's current working directory. Omit this flag to use the built-in default configuration values. Command-line flags override configuration from this file.") @@ -320,8 +320,18 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) { fs.MarkDeprecated("experimental-allocatable-ignore-eviction", "will be removed in 1.25 or later.") } +// addContainerRuntimeFlags adds flags to the container runtime, according to ContainerRuntimeOptions. +func addContainerRuntimeFlags(fs *pflag.FlagSet, o *kubeletconfig.ContainerRuntimeOptions) { + // General settings. + fs.StringVar(&o.RuntimeCgroups, "runtime-cgroups", o.RuntimeCgroups, "Optional absolute name of cgroups to create and run the runtime in.") + + // Image credential provider settings. + fs.StringVar(&o.ImageCredentialProviderConfigPath, "image-credential-provider-config", o.ImageCredentialProviderConfigPath, "Path to a credential provider plugin config file (JSON/YAML/YML) or a directory of such files (merged in lexicographical order; non-recursive search).") + fs.StringVar(&o.ImageCredentialProviderBinDir, "image-credential-provider-bin-dir", o.ImageCredentialProviderBinDir, "The path to the directory where credential provider plugin binaries are located.") +} + // AddKubeletConfigFlags adds flags for a specific kubeletconfig.KubeletConfiguration to the specified FlagSet -func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfiguration) { +func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfigapi.KubeletConfiguration) { fs := pflag.NewFlagSet("", pflag.ExitOnError) defer func() { // All KubeletConfiguration flags are now deprecated, and any new flags that point to diff --git a/cmd/kubelet/app/options/options_test.go b/cmd/kubelet/app/options/options_test.go index 3ec297cd17272..a229d812d5690 100644 --- a/cmd/kubelet/app/options/options_test.go +++ b/cmd/kubelet/app/options/options_test.go @@ -25,7 +25,7 @@ import ( "github.com/spf13/pflag" cliflag "k8s.io/component-base/cli/flag" - "k8s.io/kubernetes/pkg/kubelet/config" + "k8s.io/kubernetes/pkg/kubelet/kubeletconfig" ) func newKubeletServerOrDie() *KubeletServer { @@ -179,7 +179,7 @@ func TestValidateKubeletFlags(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateKubeletFlags(&KubeletFlags{ - ContainerRuntimeOptions: config.ContainerRuntimeOptions{}, + ContainerRuntimeOptions: kubeletconfig.ContainerRuntimeOptions{}, NodeLabels: tt.labels, }) diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 5397fa6738449..65174afad7c3b 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -35,6 +35,7 @@ import ( "time" "github.com/coreos/go-systemd/v22/daemon" + "github.com/go-logr/logr" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.opentelemetry.io/otel" @@ -64,6 +65,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/wait" genericapiserver "k8s.io/apiserver/pkg/server" + "k8s.io/apiserver/pkg/server/flagz" "k8s.io/apiserver/pkg/server/healthz" utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" @@ -86,7 +88,6 @@ import ( "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" zpagesfeatures "k8s.io/component-base/zpages/features" - "k8s.io/component-base/zpages/flagz" nodeutil "k8s.io/component-helpers/node/util" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1" @@ -209,11 +210,6 @@ is checked every 20 seconds (also configurable with a flag).`, return fmt.Errorf("failed to validate kubelet flags: %w", err) } - if cleanFlagSet.Changed("pod-infra-container-image") { - logger.Info("--pod-infra-container-image will not be pruned by the image garbage collector in kubelet and should also be set in the remote runtime") - _ = cmd.Flags().MarkDeprecated("pod-infra-container-image", "--pod-infra-container-image will be removed in 1.35. Image garbage collector will get sandbox image information from CRI.") - } - // load kubelet config file, if provided if len(kubeletFlags.KubeletConfigFile) > 0 { kubeletConfig, err = loadConfigFile(ctx, kubeletFlags.KubeletConfigFile) @@ -583,14 +579,14 @@ func makeEventRecorder(ctx context.Context, kubeDeps *kubelet.Dependencies, node } } -func getReservedCPUs(machineInfo *cadvisorapi.MachineInfo, cpus string) (cpuset.CPUSet, error) { +func getReservedCPUs(logger logr.Logger, machineInfo *cadvisorapi.MachineInfo, cpus string) (cpuset.CPUSet, error) { emptyCPUSet := cpuset.New() if cpus == "" { return emptyCPUSet, nil } - topo, err := topology.Discover(machineInfo) + topo, err := topology.Discover(logger, machineInfo) if err != nil { return emptyCPUSet, fmt.Errorf("unable to discover CPU topology info: %s", err) } @@ -606,17 +602,15 @@ func getReservedCPUs(machineInfo *cadvisorapi.MachineInfo, cpus string) (cpuset. } func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Dependencies, featureGate featuregate.FeatureGate) (err error) { - if utilfeature.DefaultFeatureGate.Enabled(features.SystemdWatchdog) { - // NewHealthChecker returns an error indicating that the watchdog is configured but the configuration is incorrect, - // the kubelet will not be started. - healthChecker, err := watchdog.NewHealthChecker() - if err != nil { - return fmt.Errorf("create health checker: %w", err) - } - kubeDeps.HealthChecker = healthChecker - healthChecker.Start(ctx) - } logger := klog.FromContext(ctx) + // NewHealthChecker returns an error indicating that the watchdog is configured but the configuration is incorrect, + // the kubelet will not be started. + healthChecker, err := watchdog.NewHealthChecker(logger) + if err != nil { + return fmt.Errorf("create health checker: %w", err) + } + kubeDeps.HealthChecker = healthChecker + healthChecker.Start(ctx) // Set global feature gates based on the value on the initial KubeletServer err = utilfeature.DefaultMutableFeatureGate.SetFromMap(s.KubeletConfiguration.FeatureGates) if err != nil { @@ -650,12 +644,6 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend } } - // Register current configuration with /configz endpoint - err = initConfigz(ctx, &s.KubeletConfiguration) - if err != nil { - logger.Error(err, "Failed to register kubelet configuration with configz") - } - if len(s.ShowHiddenMetricsForVersion) > 0 { metrics.SetShowHidden() } @@ -745,10 +733,16 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend return err } + // Register current configuration with /configz endpoint + err = initConfigz(ctx, &s.KubeletConfiguration) + if err != nil { + logger.Error(err, "Failed to register kubelet configuration with configz") + } + var cgroupRoots []string nodeAllocatableRoot := cm.NodeAllocatableRoot(s.CgroupRoot, s.CgroupsPerQOS, s.CgroupDriver) cgroupRoots = append(cgroupRoots, nodeAllocatableRoot) - kubeletCgroup, err := cm.GetKubeletContainer(s.KubeletCgroups) + kubeletCgroup, err := cm.GetKubeletContainer(logger, s.KubeletCgroups) if err != nil { logger.Info("Failed to get the kubelet's cgroup. Kubelet system container metrics may be missing", "err", err) } else if kubeletCgroup != "" { @@ -792,7 +786,7 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend if err != nil { return err } - reservedSystemCPUs, err := getReservedCPUs(machineInfo, s.ReservedSystemCPUs) + reservedSystemCPUs, err := getReservedCPUs(logger, machineInfo, s.ReservedSystemCPUs) if err != nil { return err } @@ -850,6 +844,7 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend } kubeDeps.ContainerManager, err = cm.NewContainerManager( + ctx, kubeDeps.Mounter, kubeDeps.CAdvisorInterface, cm.NodeConfig{ @@ -960,7 +955,7 @@ func buildKubeletClientConfig(ctx context.Context, s *options.KubeletServer, tp // bootstrap the cert manager with the contents of the initial client config. logger.Info("Client rotation is on, will bootstrap in background") - certConfig, clientConfig, err := bootstrap.LoadClientConfig(s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory) + certConfig, clientConfig, err := bootstrap.LoadClientConfig(logger, s.KubeConfig, s.BootstrapKubeconfig, s.CertDirectory) if err != nil { return nil, nil, err } @@ -998,7 +993,7 @@ func buildKubeletClientConfig(ctx context.Context, s *options.KubeletServer, tp // we set exitAfter to five minutes because we use this client configuration to request new certs - if we are unable // to request new certs, we will be unable to continue normal operation. Exiting the process allows a wrapper // or the bootstrapping credentials to potentially lay down new initial config. - closeAllConns, err := kubeletcertificate.UpdateTransport(wait.NeverStop, transportConfig, clientCertificateManager, 5*time.Minute) + closeAllConns, err := kubeletcertificate.UpdateTransport(logger, wait.NeverStop, transportConfig, clientCertificateManager, 5*time.Minute) if err != nil { return nil, nil, err } @@ -1276,10 +1271,10 @@ func startKubelet(ctx context.Context, k kubelet.Bootstrap, podCfg *config.PodCo // start the kubelet server if enableServer { - go k.ListenAndServe(kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth, kubeDeps.TracerProvider) + go k.ListenAndServe(ctx, kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth, kubeDeps.TracerProvider) } if kubeCfg.ReadOnlyPort > 0 { - go k.ListenAndServeReadOnly(netutils.ParseIPSloppy(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort), kubeDeps.TracerProvider) + go k.ListenAndServeReadOnly(ctx, netutils.ParseIPSloppy(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort), kubeDeps.TracerProvider) } go k.ListenAndServePodResources(ctx) } diff --git a/cmd/kubelet/app/server_bootstrap_test.go b/cmd/kubelet/app/server_bootstrap_test.go index 95c7475e52858..b7162920d5c78 100644 --- a/cmd/kubelet/app/server_bootstrap_test.go +++ b/cmd/kubelet/app/server_bootstrap_test.go @@ -38,8 +38,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + utilfeature "k8s.io/apiserver/pkg/util/feature" + clientfeatures "k8s.io/client-go/features" restclient "k8s.io/client-go/rest" certutil "k8s.io/client-go/util/cert" + "k8s.io/component-base/featuregate" capihelper "k8s.io/kubernetes/pkg/apis/certificates/v1" "k8s.io/kubernetes/pkg/controller/certificates/authority" ) @@ -350,6 +354,49 @@ func (s *csrSimulator) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write(data) + case utilfeature.DefaultFeatureGate.Enabled(featuregate.Feature(clientfeatures.WatchListClient)) && req.Method == http.MethodGet && req.URL.Path == "/apis/certificates.k8s.io/v1/certificatesigningrequests" && req.URL.RawQuery == "fieldSelector=metadata.name%3Dtest-csr&resourceVersionMatch=NotOlderThan&sendInitialEvents=true&watch=true": + // TODO(#115478): remove the list & watch cases above when WatchListClient FG is removed + if s.csr == nil { + t.Fatalf("no csr") + } + csr := &metav1.WatchEvent{ + Type: string(watch.Added), + Object: runtime.RawExtension{ + Raw: mustMarshal(s.csr.DeepCopy()), + }, + } + + bookmark := &metav1.WatchEvent{ + Type: string(watch.Bookmark), + Object: runtime.RawExtension{ + Raw: mustMarshal(&certapi.CertificateSigningRequest{ + TypeMeta: metav1.TypeMeta{ + Kind: "CertificateSigningRequest", + APIVersion: "certificates.k8s.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + metav1.InitialEventsAnnotationKey: "true", + }, + }, + }), + }, + } + + flusher, ok := w.(http.Flusher) + if !ok { + t.Fatal("http.ResponseWriter doesn't support http.Flusher") + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + if _, err := w.Write(mustMarshal(csr)); err != nil { + t.Fatal(err) + } + if _, err := w.Write(mustMarshal(bookmark)); err != nil { + t.Fatal(err) + } + flusher.Flush() + default: t.Fatalf("unexpected request: %s %s", req.Method, req.URL) } diff --git a/cmd/kubelet/app/server_windows.go b/cmd/kubelet/app/server_windows.go index 8441ca85980fc..781cd95cca9bf 100644 --- a/cmd/kubelet/app/server_windows.go +++ b/cmd/kubelet/app/server_windows.go @@ -38,7 +38,7 @@ func checkPermissions(ctx context.Context) error { // For Windows user.UserName contains the login name and user.Name contains // the user's display name - https://pkg.go.dev/os/user#User - logger.Info("Kubelet is running as", "login name", u.Username, "dispaly name", u.Name) + logger.Info("Kubelet is running as", "login name", u.Username, "display name", u.Name) if !windows.GetCurrentProcessToken().IsElevated() { return errors.New("kubelet needs to run with elevated permissions!") diff --git a/go.mod b/go.mod index fc77d8ff2d39f..164c725fe77a7 100644 --- a/go.mod +++ b/go.mod @@ -6,33 +6,31 @@ module k8s.io/kubernetes -go 1.24.0 +go 1.25.0 -godebug default=go1.24 +godebug default=go1.25 require ( bitbucket.org/bertimus9/systemstat v0.5.0 github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab github.com/Microsoft/go-winio v0.6.2 - github.com/Microsoft/hnslib v0.1.1 + github.com/Microsoft/hnslib v0.1.2 github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 - github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/blang/semver/v4 v4.0.0 github.com/container-storage-interface/spec v1.9.0 - github.com/coredns/corefile-migration v1.0.26 + github.com/coredns/corefile-migration v1.0.29 github.com/coreos/go-oidc v2.3.0+incompatible github.com/coreos/go-systemd/v22 v22.5.0 github.com/cpuguy83/go-md2man/v2 v2.0.6 - github.com/cyphar/filepath-securejoin v0.4.1 + github.com/cyphar/filepath-securejoin v0.6.0 github.com/distribution/reference v0.6.0 github.com/docker/go-units v0.5.0 github.com/emicklei/go-restful/v3 v3.12.2 github.com/fsnotify/fsnotify v1.9.0 - github.com/go-logr/logr v1.4.2 + github.com/go-logr/logr v1.4.3 github.com/go-openapi/jsonreference v0.20.2 github.com/godbus/dbus/v5 v5.1.0 - github.com/gogo/protobuf v1.3.2 - github.com/google/cadvisor v0.52.1 + github.com/google/cadvisor v0.53.0 github.com/google/cel-go v0.26.0 github.com/google/gnostic-models v0.7.0 github.com/google/go-cmp v0.7.0 @@ -44,78 +42,78 @@ require ( github.com/moby/sys/userns v0.1.0 github.com/mrunalp/fileutils v0.5.1 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 - github.com/onsi/ginkgo/v2 v2.21.0 - github.com/onsi/gomega v1.35.1 + github.com/onsi/ginkgo/v2 v2.25.1 + github.com/onsi/gomega v1.38.2 github.com/opencontainers/cgroups v0.0.3 - github.com/opencontainers/runc v1.2.5 - github.com/opencontainers/selinux v1.11.1 - github.com/openshift-eng/openshift-tests-extension v0.0.0-20250916161632-d81c09058835 - github.com/openshift/api v0.0.0-20251214014457-bfa868a22401 - github.com/openshift/apiserver-library-go v0.0.0-20251015164739-79d04067059d - github.com/openshift/client-go v0.0.0-20251205093018-96a6cbc1420c - github.com/openshift/library-go v0.0.0-20251015151611-6fc7a74b67c5 + github.com/opencontainers/selinux v1.13.0 + github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818 + github.com/openshift/api v0.0.0-20260211194905-f62f47eaf03d + github.com/openshift/apiserver-library-go v0.0.0-20260211203915-ee5ba89cd34b + github.com/openshift/client-go v0.0.0-20260211200652-3585e10bcc17 + github.com/openshift/library-go v0.0.0-20260211202355-e615a1f60a34 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - github.com/prometheus/client_golang v1.22.0 - github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.62.0 + github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/client_model v0.6.2 + github.com/prometheus/common v0.66.1 github.com/robfig/cron/v3 v3.0.1 - github.com/spf13/cobra v1.9.1 - github.com/spf13/pflag v1.0.6 - github.com/stretchr/testify v1.10.0 + github.com/spf13/cobra v1.10.0 + github.com/spf13/pflag v1.0.9 + github.com/stretchr/testify v1.11.1 github.com/vishvananda/netlink v1.3.1 github.com/vishvananda/netns v0.0.5 - go.etcd.io/etcd/api/v3 v3.6.4 - go.etcd.io/etcd/client/pkg/v3 v3.6.4 - go.etcd.io/etcd/client/v3 v3.6.4 + go.etcd.io/etcd/api/v3 v3.6.5 + go.etcd.io/etcd/client/pkg/v3 v3.6.5 + go.etcd.io/etcd/client/v3 v3.6.5 go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 - go.opentelemetry.io/otel v1.35.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 + go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 - go.opentelemetry.io/otel/metric v1.35.0 - go.opentelemetry.io/otel/sdk v1.34.0 - go.opentelemetry.io/otel/trace v1.35.0 + go.opentelemetry.io/otel/metric v1.36.0 + go.opentelemetry.io/otel/sdk v1.36.0 + go.opentelemetry.io/otel/trace v1.36.0 go.opentelemetry.io/proto/otlp v1.5.0 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.27.0 - go.yaml.in/yaml/v2 v2.4.2 - golang.org/x/crypto v0.42.0 - golang.org/x/net v0.43.0 - golang.org/x/oauth2 v0.27.0 - golang.org/x/sync v0.17.0 - golang.org/x/sys v0.36.0 - golang.org/x/term v0.35.0 + go.yaml.in/yaml/v2 v2.4.3 + golang.org/x/crypto v0.45.0 + golang.org/x/net v0.47.0 + golang.org/x/oauth2 v0.30.0 + golang.org/x/sync v0.18.0 + golang.org/x/sys v0.38.0 + golang.org/x/term v0.37.0 + golang.org/x/text v0.31.0 golang.org/x/time v0.9.0 - golang.org/x/tools v0.36.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb - google.golang.org/grpc v1.72.1 - google.golang.org/protobuf v1.36.5 - gopkg.in/evanphx/json-patch.v4 v4.12.0 + golang.org/x/tools v0.38.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a + google.golang.org/grpc v1.72.2 + google.golang.org/protobuf v1.36.8 + gopkg.in/evanphx/json-patch.v4 v4.13.0 gopkg.in/go-jose/go-jose.v2 v2.6.3 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 - k8s.io/apimachinery v0.34.1 - k8s.io/apiserver v0.34.1 + k8s.io/api v0.35.1 + k8s.io/apiextensions-apiserver v0.35.1 + k8s.io/apimachinery v0.35.1 + k8s.io/apiserver v0.35.1 k8s.io/cli-runtime v0.0.0 - k8s.io/client-go v0.34.1 + k8s.io/client-go v0.35.1 k8s.io/cloud-provider v0.0.0 k8s.io/cluster-bootstrap v0.0.0 - k8s.io/code-generator v0.34.1 - k8s.io/component-base v0.34.1 - k8s.io/component-helpers v0.32.1 + k8s.io/code-generator v0.35.1 + k8s.io/component-base v0.35.1 + k8s.io/component-helpers v0.35.0 k8s.io/controller-manager v0.32.1 k8s.io/cri-api v0.0.0 k8s.io/cri-client v0.0.0 k8s.io/csi-translation-lib v0.0.0 - k8s.io/dynamic-resource-allocation v0.0.0 + k8s.io/dynamic-resource-allocation v0.35.0 k8s.io/endpointslice v0.0.0 k8s.io/externaljwt v0.0.0 k8s.io/klog/v2 v2.130.1 - k8s.io/kms v0.34.1 - k8s.io/kube-aggregator v0.34.1 + k8s.io/kms v0.35.1 + k8s.io/kube-aggregator v0.35.1 k8s.io/kube-controller-manager v0.0.0 - k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 k8s.io/kube-proxy v0.0.0 k8s.io/kube-scheduler v0.0.0 k8s.io/kubectl v0.0.0 @@ -124,9 +122,9 @@ require ( k8s.io/mount-utils v0.0.0 k8s.io/pod-security-admission v0.0.0 k8s.io/sample-apiserver v0.0.0 - k8s.io/system-validators v1.10.2 - k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 + k8s.io/system-validators v1.12.1 + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 sigs.k8s.io/knftables v0.0.17 sigs.k8s.io/randfill v1.0.0 sigs.k8s.io/structured-merge-diff/v6 v6.3.0 @@ -135,21 +133,23 @@ require ( require ( cel.dev/expr v0.24.0 // indirect + cyphar.com/go-pathrs v0.2.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/containerd/containerd/api v1.8.0 // indirect + github.com/containerd/containerd/api v1.9.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/ttrpc v1.2.6 // indirect - github.com/containerd/typeurl/v2 v2.2.2 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/coredns/caddy v1.1.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -167,10 +167,11 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect @@ -197,11 +198,11 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect + github.com/opencontainers/runtime-spec v1.2.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect @@ -211,9 +212,9 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.etcd.io/bbolt v1.4.2 // indirect - go.etcd.io/etcd/pkg/v3 v3.6.4 // indirect - go.etcd.io/etcd/server/v3 v3.6.4 // indirect + go.etcd.io/bbolt v1.4.3 // indirect + go.etcd.io/etcd/pkg/v3 v3.6.5 // indirect + go.etcd.io/etcd/server/v3 v3.6.5 // indirect go.etcd.io/raft/v3 v3.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect @@ -222,12 +223,11 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/mod v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect + k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect sigs.k8s.io/kustomize/api v0.20.1 // indirect sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 // indirect @@ -235,8 +235,11 @@ require ( ) replace ( - github.com/google/cadvisor => github.com/openshift/google-cadvisor v0.52.1-openshift-4.21-1 - github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 + github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251120221002-696928a6a0d7 + github.com/openshift/api => github.com/jacobsee/openshift-api v0.0.0-20260211194905-f62f47eaf03d + github.com/openshift/apiserver-library-go => github.com/jacobsee/apiserver-library-go v0.0.0-20260211203915-ee5ba89cd34b + github.com/openshift/client-go => github.com/jacobsee/client-go v0.0.0-20260211200652-3585e10bcc17 + github.com/openshift/library-go => github.com/jacobsee/library-go v0.0.0-20260211202355-e615a1f60a34 k8s.io/api => ./staging/src/k8s.io/api k8s.io/apiextensions-apiserver => ./staging/src/k8s.io/apiextensions-apiserver k8s.io/apimachinery => ./staging/src/k8s.io/apimachinery diff --git a/go.sum b/go.sum index 56a5c1b89c522..d5a16fda3cb41 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,9 @@ bitbucket.org/bertimus9/systemstat v0.5.0/go.mod h1:EkUWPp8lKFPMXP8vnbpT5JDI0W/s buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.4-20250130201111-63bb56e20495.1/go.mod h1:novQBstnxcGpfKf8qGRATqn1anQKwMJIbH5Q581jibU= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= +cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= +cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= @@ -13,10 +15,12 @@ github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1 github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hnslib v0.1.1 h1:JsZy681SnvSOUAfCZVAxkX4LgQGp+CZZwPbLV0/pdF8= -github.com/Microsoft/hnslib v0.1.1/go.mod h1:DRQR4IjLae6WHYVhW7uqe44hmFUiNhmaWA+jwMbz5tM= +github.com/Microsoft/hnslib v0.1.2 h1:CshjwTQsNx1o7BIA1XO8HtgDsiCqn+b6kGjL/tIxXQQ= +github.com/Microsoft/hnslib v0.1.2/go.mod h1:5vTyBey4N/VI2ZTNh2gdWhkPMefSbCFYjpvVwye+qtI= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/RangelReale/osincli v0.0.0-20160924135400-fababb0555f2/go.mod h1:XyjUkMA8GN+tOOPXvnbi3XuRxWFvTJntqvTFnjmhzbk= @@ -32,19 +36,19 @@ github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloD github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/config v1.27.24/go.mod h1:aXzi6QJTuQRVVusAO8/NxpdTeTyr/wRcybdDtfUwJSs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.24/go.mod h1:Hld7tmnAkoBQdTMNYZGzztzKRdA4fCdn9L83LOoigac= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -56,7 +60,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/checkpoint-restore/go-criu/v6 v6.3.0/go.mod h1:rrRTN/uSwY2X+BPRl/gkulo9gsKOSAeVp9/K2tv7xZI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/cilium/ebpf v0.17.3/go.mod h1:G5EDHij8yiLzaqn0WjyfJHvRa+3aDlReIaLVRMvOyJk= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= @@ -64,23 +67,22 @@ github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD9 github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/container-storage-interface/spec v1.9.0 h1:zKtX4STsq31Knz3gciCYCi1SXtO2HJDecIjDVboYavY= github.com/container-storage-interface/spec v1.9.0/go.mod h1:ZfDu+3ZRyeVqxZM0Ds19MVLkN2d1XJ5MAfi1L3VjlT0= -github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= -github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= +github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0= +github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4= -github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= -github.com/containerd/typeurl/v2 v2.2.2 h1:3jN/k2ysKuPCsln5Qv8bzR9cxal8XjkxPogJfSNO31k= -github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= -github.com/coredns/corefile-migration v1.0.26 h1:xiiEkVB1Dwolb24pkeDUDBfygV9/XsOSq79yFCrhptY= -github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.29 h1:g4cPYMXXDDs9uLE2gFYrJaPBuUAR07eEMGyh9JBE13w= +github.com/coredns/corefile-migration v1.0.29/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0= github.com/coreos/go-oidc v2.3.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -92,8 +94,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= +github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -101,8 +103,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/distribution/distribution/v3 v3.0.0-20230511163743-f7717b7855ca/go.mod h1:t1IxPNGdTGez+YGKyJyQrtSSqisfMIm1hnFhvMPlxtE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU= -github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= @@ -132,6 +134,12 @@ github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8 github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -140,8 +148,8 @@ github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh1 github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU= github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -156,6 +164,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -175,6 +185,8 @@ github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2 github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cadvisor v0.53.0 h1:pmveUw2VBlr/T2SBE9Fsp8gdLhKWyOBkECGbaas9mcI= +github.com/google/cadvisor v0.53.0/go.mod h1:Tz3zf/exzFfdWd1T/U/9eNst0ZR2C6CIV62LJATj5tg= github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= @@ -182,8 +194,8 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -211,6 +223,14 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995 h1:GtGlZy0FQTUGKSGVhzFZixkUXnpRj7s1rKEegNZcy9Y= github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= +github.com/jacobsee/apiserver-library-go v0.0.0-20260211203915-ee5ba89cd34b h1:26tupoJhzGhK5F178nHcRKaaQdgH5sFyjRT/UZ4HuZY= +github.com/jacobsee/apiserver-library-go v0.0.0-20260211203915-ee5ba89cd34b/go.mod h1:BSskwbEaYqCCoSeqjv1S7Mw8IANCYzAVPtUXLzQ7SJA= +github.com/jacobsee/client-go v0.0.0-20260211200652-3585e10bcc17 h1:fbl50zu3Ol4/FUSorOUD5xPqvXD94OvREGNacymXOgw= +github.com/jacobsee/client-go v0.0.0-20260211200652-3585e10bcc17/go.mod h1:mCcIpR1nUVJvuLFKl7etbRLixUhJn4XjufJdfzv8Xsc= +github.com/jacobsee/library-go v0.0.0-20260211202355-e615a1f60a34 h1:iCDeH9PliuQ4IL5NhQhZ1GcXxdlIiuiIed7hK5d5shw= +github.com/jacobsee/library-go v0.0.0-20260211202355-e615a1f60a34/go.mod h1:mp92ELYoE9GHXRj+jJhp4I9KsFfS5c7WIi5k4RmkPYY= +github.com/jacobsee/openshift-api v0.0.0-20260211194905-f62f47eaf03d h1:kqSuLRGP/BS1qyFXP8X9t+aZehk/yF853PFu2e1og0U= +github.com/jacobsee/openshift-api v0.0.0-20260211194905-f62f47eaf03d/go.mod h1:u/qMsjN4RliFQwfk4bMEdF1lTjkagrlxPcZ/G9KIVuE= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -227,6 +247,8 @@ github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbd github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -254,7 +276,11 @@ github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffkt github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -265,6 +291,7 @@ github.com/moby/ipvs v1.1.0 h1:ONN4pGaZQgAx+1Scz5RvWV4Q7Gb+mvfRh3NsPS+1XQQ= github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= @@ -290,35 +317,24 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/cgroups v0.0.3 h1:Jc9dWh/0YLGjdy6J/9Ln8NM5BfTA4W2BY0GMozy3aDU= github.com/opencontainers/cgroups v0.0.3/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opencontainers/runc v1.2.5 h1:8KAkq3Wrem8bApgOHyhRI/8IeLXIfmZ6Qaw6DNSLnA4= -github.com/opencontainers/runc v1.2.5/go.mod h1:dOQeFo29xZKBNeRBI0B19mJtfHv68YgCTh1X+YphA+4= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8= -github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= -github.com/openshift-eng/openshift-tests-extension v0.0.0-20250916161632-d81c09058835 h1:rkqIIfdYYkasXbF2XKVgh/3f1mhjSQK9By8WtVMgYo8= -github.com/openshift-eng/openshift-tests-extension v0.0.0-20250916161632-d81c09058835/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M= -github.com/openshift/api v0.0.0-20251214014457-bfa868a22401 h1:goMf6pBtRFSQaVElFk6K+GIAqnv7O84p7PJHH6pDz/E= -github.com/openshift/api v0.0.0-20251214014457-bfa868a22401/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= -github.com/openshift/apiserver-library-go v0.0.0-20251015164739-79d04067059d h1:Mfya3RxHWvidOrKyHj3bmFn5x2B89DLZIvDAhwm+C2s= -github.com/openshift/apiserver-library-go v0.0.0-20251015164739-79d04067059d/go.mod h1:zm2/rIUp0p83pz0/1kkSoKTqhTr3uUKSKQ9fP7Z3g7Y= +github.com/opencontainers/runc v1.3.0/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs= +github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= +github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.13.0 h1:Zza88GWezyT7RLql12URvoxsbLfjFx988+LGaWfbL84= +github.com/opencontainers/selinux v1.13.0/go.mod h1:XxWTed+A/s5NNq4GmYScVy+9jzXhGBVEOAyucdRUY8s= +github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818 h1:jJLE/aCAqDf8U4wc3bE1IEKgIxbb0ICjCNVFA49x/8s= +github.com/openshift-eng/openshift-tests-extension v0.0.0-20260127124016-0fed2b824818/go.mod h1:6gkP5f2HL0meusT0Aim8icAspcD1cG055xxBZ9yC68M= github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= -github.com/openshift/client-go v0.0.0-20251205093018-96a6cbc1420c h1:TBE0Gl+oCo/SNEhLKZQNNH/SWHXrpGyhAw7P0lAqdHg= -github.com/openshift/client-go v0.0.0-20251205093018-96a6cbc1420c/go.mod h1:IsynOWZAfdH+BgWimcFQRtI41Id9sgdhsCEjIk8ACLw= -github.com/openshift/google-cadvisor v0.52.1-openshift-4.21-1 h1:GNwO7gdyk6kAbFwIRo0T8NOTgxKYA2J6tSAtVMYavY0= -github.com/openshift/google-cadvisor v0.52.1-openshift-4.21-1/go.mod h1:OAhPcx1nOm5YwMh/JhpUOMKyv1YKLRtS9KgzWPndHmA= -github.com/openshift/library-go v0.0.0-20251015151611-6fc7a74b67c5 h1:bANtDc8SgetSK4nQehf59x3+H9FqVJCprgjs49/OTg0= -github.com/openshift/library-go v0.0.0-20251015151611-6fc7a74b67c5/go.mod h1:OlFFws1AO51uzfc48MsStGE4SFMWlMZD0+f5a/zCtKI= -github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 h1:PMTgifBcBRLJJiM+LgSzPDTk9/Rx4qS09OUrfpY6GBQ= -github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251120221002-696928a6a0d7 h1:02E4Ttpu+7yCQLQxtY42JfcfHU7TBGnje6uB2ytBSdU= +github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251120221002-696928a6a0d7/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -331,25 +347,24 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.74.0/go.mod h1:wAR5JopumPtAZnu0Cjv2PSqV4p4QB09LMhc6fZZTXuA= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -357,10 +372,11 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.0 h1:a5/WeUlSDCvV5a45ljW2ZFtV0bTDpkfSAj3uqB6Sc+0= +github.com/spf13/cobra v1.10.0/go.mod h1:9dhySC7dnTtEiqzmqfkLj47BslqLCUPMXjG2lj/NgoE= +github.com/spf13/pflag v1.0.8/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= @@ -376,12 +392,18 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= -github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= @@ -397,18 +419,18 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= -go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= -go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= -go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= -go.etcd.io/etcd/pkg/v3 v3.6.4 h1:fy8bmXIec1Q35/jRZ0KOes8vuFxbvdN0aAFqmEfJZWA= -go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= -go.etcd.io/etcd/server/v3 v3.6.4 h1:LsCA7CzjVt+8WGrdsnh6RhC0XqCsLkBly3ve5rTxMAU= -go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA= +go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ= +go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8= +go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk= +go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U= +go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo= +go.etcd.io/etcd/pkg/v3 v3.6.5 h1:byxWB4AqIKI4SBmquZUG1WGtvMfMaorXFoCcFbVeoxM= +go.etcd.io/etcd/pkg/v3 v3.6.5/go.mod h1:uqrXrzmMIJDEy5j00bCqhVLzR5jEJIwDp5wTlLwPGOU= +go.etcd.io/etcd/server/v3 v3.6.5 h1:4RbUb1Bd4y1WkBHmuF+cZII83JNQMuNXzyjwigQ06y0= +go.etcd.io/etcd/server/v3 v3.6.5/go.mod h1:PLuhyVXz8WWRhzXDsl3A3zv/+aK9e4A9lpQkqawIaH0= go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o= go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= @@ -419,65 +441,66 @@ go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelr go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0/go.mod h1:uq8DrRaen3suIWTpdR/JNHCGpurSvMv9D5Nr5CU5TXc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/b3 v1.19.0 h1:ulz44cpm6V5oAeg5Aw9HyqGFMS6XM7untlMEhD7YzzA= go.opentelemetry.io/contrib/propagators/b3 v1.19.0/go.mod h1:OzCmE2IVS+asTI+odXQstRGVfXQ4bXv9nMBRK0nNyqQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -490,27 +513,27 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= -golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= -golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -519,17 +542,17 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -542,20 +565,20 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= -k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b h1:gMplByicHV/TJBizHd9aVEsTYoJBnnUAT5MHlTkbjhQ= +k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b/go.mod h1:CgujABENc3KuTrcsdpGmrrASjtQsWCT7R99mEV4U/fM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= -k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= -k8s.io/system-validators v1.10.2 h1:7rC7VdrQCaM55E08Pw3I1v1Op9ObLxdKAu5Ff5hIPwY= -k8s.io/system-validators v1.10.2/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0FX9Wmw= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= -k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/system-validators v1.12.1 h1:AY1+COTLJN/Sj0w9QzH1H0yvyF3Kl6CguMnh32WlcUU= +k8s.io/system-validators v1.12.1/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0FX9Wmw= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= -sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/knftables v0.0.17 h1:wGchTyRF/iGTIjd+vRaR1m676HM7jB8soFtyr/148ic= sigs.k8s.io/knftables v0.0.17/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96/go.mod h1:EOBQyBowOUsd7U4CJnMHNE0ri+zCXyouGdLwC/jZU+I= diff --git a/go.work b/go.work index 092a6c8da38ae..bd119eefdea91 100644 --- a/go.work +++ b/go.work @@ -1,8 +1,8 @@ // This is a generated file. Do not edit directly. -go 1.24.0 +go 1.25.0 -godebug default=go1.24 +godebug default=go1.25 use ( . diff --git a/go.work.sum b/go.work.sum index 6a276ef44e241..8e279418360e0 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,30 +1,28 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.4-20250130201111-63bb56e20495.1 h1:4erM3WLgEG/HIBrpBDmRbs1puhd7p0z7kNXDuhHthwM= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= github.com/RangelReale/osincli v0.0.0-20160924135400-fababb0555f2 h1:x8Brv0YNEe6jY3V/hQglIG2nd8g5E2Zj5ubGKkPQctQ= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= -github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o= -github.com/aws/aws-sdk-go-v2/config v1.27.24 h1:NM9XicZ5o1CBU/MZaHwFtimRpWx9ohAUAqkG6AqSqPo= -github.com/aws/aws-sdk-go-v2/credentials v1.17.24 h1:YclAsrnb1/GTQNt2nzv+756Iw4mF8AOzcDfweWwwm/M= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 h1:ORnrOK0C4WmYV/uYt3koHEWBLYsRDwk2Np+eEoyV4Z0= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= +github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/bufbuild/protovalidate-go v0.9.1 h1:cdrIA33994yCcJyEIZRL36ZGTe9UDM/WHs5MBHEimiE= -github.com/checkpoint-restore/go-criu/v6 v6.3.0 h1:mIdrSO2cPNWQY1truPg6uHLXyKHk3Z5Odx4wjKOASzA= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/cilium/ebpf v0.17.3 h1:FnP4r16PWYSE4ux6zN+//jMcW4nMVRvuTLVTvCjyyjg= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= github.com/distribution/distribution/v3 v3.0.0-20230511163743-f7717b7855ca h1:yGaIDzPWkgU+yRvI2x/rGdOU1hl6bLZzm0mETEUSHwk= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= @@ -56,9 +54,11 @@ github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHz github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/opencontainers/runc v1.3.0 h1:cvP7xbEvD0QQAs0nZKLzkVog2OPZhI/V2w3WmTmUSXI= github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee h1:+Sp5GGnjHDhT/a/nQ1xdp43UscBMr7G5wxsYotyhzJ4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= @@ -68,17 +68,15 @@ github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/seccomp/libseccomp-golang v0.10.0 h1:aA4bp+/Zzi0BnWZ2F1wgNBs5gTpm+na2rWM6M9YjLpY= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= -github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA= go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= -golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488 h1:3doPGa+Gg4snce233aCWnbZVFsyFMo/dR40KK/6skyE= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 h1:PFWFSkpArPNJxFX4ZKWAk9NSeRoZaXschn+ULa4xVek= diff --git a/hack/_update-generated-proto-bindings-dockerized.sh b/hack/_update-generated-proto-bindings-dockerized.sh index 9c7a7d28822a4..55cb70a650daa 100755 --- a/hack/_update-generated-proto-bindings-dockerized.sh +++ b/hack/_update-generated-proto-bindings-dockerized.sh @@ -27,12 +27,10 @@ source "${KUBE_ROOT}/hack/lib/protoc.sh" source "${KUBE_ROOT}/hack/lib/util.sh" if (( $# < 2 )); then - echo "usage: $0 [gogo|protoc] ..." + echo "usage: $0 ..." exit 1 fi -generator=$1; shift - kube::protoc::check_protoc for api; do @@ -49,16 +47,6 @@ for api; do for file in "${protos[@]}"; do dir="$(dirname "${file}")" - case "${generator}" in - gogo) - kube::protoc::generate_proto_gogo "${dir}" - ;; - protoc) - kube::protoc::generate_proto "${dir}" - ;; - *) - echo "Unknown generator ${generator}" >&2 - exit 1 - esac + kube::protoc::generate_proto "${dir}" done done diff --git a/hack/boilerplate/boilerplate.Dockerfile.txt b/hack/boilerplate/boilerplate.Dockerfile.txt index 384f325abf324..069e282bc855a 100644 --- a/hack/boilerplate/boilerplate.Dockerfile.txt +++ b/hack/boilerplate/boilerplate.Dockerfile.txt @@ -1,4 +1,4 @@ -# Copyright YEAR The Kubernetes Authors. +# Copyright 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. diff --git a/hack/boilerplate/boilerplate.Makefile.txt b/hack/boilerplate/boilerplate.Makefile.txt index 384f325abf324..069e282bc855a 100644 --- a/hack/boilerplate/boilerplate.Makefile.txt +++ b/hack/boilerplate/boilerplate.Makefile.txt @@ -1,4 +1,4 @@ -# Copyright YEAR The Kubernetes Authors. +# Copyright 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. diff --git a/hack/boilerplate/boilerplate.go.txt b/hack/boilerplate/boilerplate.go.txt index 59e740c1ee4a4..b7c650da47014 100644 --- a/hack/boilerplate/boilerplate.go.txt +++ b/hack/boilerplate/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright YEAR The Kubernetes Authors. +Copyright 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. diff --git a/hack/boilerplate/boilerplate.py b/hack/boilerplate/boilerplate.py index 65eb08b00ecbc..2e82e1e171024 100755 --- a/hack/boilerplate/boilerplate.py +++ b/hack/boilerplate/boilerplate.py @@ -103,26 +103,11 @@ def file_passes(filename, refs, regexs): # trim our file to the same number of lines as the reference file data = data[: len(ref)] - pattern = regexs["year"] - for line in data: - if pattern.search(line): - if generated: - print( - f"File {filename} has the YEAR field, but it should not be in generated file", - file=verbose_out, - ) - else: - print( - "File {filename} has the YEAR field, but missing the year of date", - file=verbose_out, - ) - return False - if not generated: - # Replace all occurrences of the regex "2014|2015|2016|2017|2018" with "YEAR" + # Remove all occurrences of the year (regex "Copyright (2014|2015|2016|2017|2018) ") pattern = regexs["date"] for i, line in enumerate(data): - data[i], found = pattern.subn("YEAR", line) + data[i], found = pattern.subn("Copyright ", line) if found != 0: break @@ -203,18 +188,18 @@ def get_files(extensions): def get_dates(): - years = datetime.datetime.now().year - return "(%s)" % "|".join(str(year) for year in range(2014, years + 1)) + # After 2025, we no longer allow new files to include the year in the copyright header. + final_year = 2025 + return " (%s) " % "|".join(str(year) for year in range(2014, final_year + 1)) def get_regexs(): regexs = {} # Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing regexs["year"] = re.compile("YEAR") - # get_dates return 2014, 2015, 2016, 2017, or 2018 until the current year - # as a regex like: "(2014|2015|2016|2017|2018)"; - # company holder names can be anything - regexs["date"] = re.compile(get_dates()) + # get_dates return 2014, 2015, 2016, 2017, ..., 2025 + # as a regex like: "(2014|2015|2016|2017|2018|...|2025)"; + regexs["date"] = re.compile("Copyright" + get_dates()) # strip the following build constraints/tags: # //go:build # // +build \n\n diff --git a/hack/boilerplate/boilerplate.py.txt b/hack/boilerplate/boilerplate.py.txt index 384f325abf324..069e282bc855a 100644 --- a/hack/boilerplate/boilerplate.py.txt +++ b/hack/boilerplate/boilerplate.py.txt @@ -1,4 +1,4 @@ -# Copyright YEAR The Kubernetes Authors. +# Copyright 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. diff --git a/hack/boilerplate/boilerplate.sh.txt b/hack/boilerplate/boilerplate.sh.txt index 384f325abf324..069e282bc855a 100644 --- a/hack/boilerplate/boilerplate.sh.txt +++ b/hack/boilerplate/boilerplate.sh.txt @@ -1,4 +1,4 @@ -# Copyright YEAR The Kubernetes Authors. +# Copyright 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. diff --git a/hack/boilerplate/boilerplate_test.py b/hack/boilerplate/boilerplate_test.py index 8fc7e4cbcb1fa..cd8f532fd6770 100644 --- a/hack/boilerplate/boilerplate_test.py +++ b/hack/boilerplate/boilerplate_test.py @@ -50,4 +50,4 @@ class Args: sys.stdout = old_stdout - self.assertEqual(output, ["././fail.go", "././fail.py"]) + self.assertEqual(output, ["././fail.go", "././fail.py", "././fail_2026.go"]) diff --git a/hack/boilerplate/test/fail_2026.go b/hack/boilerplate/test/fail_2026.go new file mode 100644 index 0000000000000..1f9623be5b7c7 --- /dev/null +++ b/hack/boilerplate/test/fail_2026.go @@ -0,0 +1,17 @@ +/* +Copyright 2026 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 test diff --git a/hack/boilerplate/test/pass_no_year.go b/hack/boilerplate/test/pass_no_year.go new file mode 100644 index 0000000000000..d4a36a26617f6 --- /dev/null +++ b/hack/boilerplate/test/pass_no_year.go @@ -0,0 +1,17 @@ +/* +Copyright 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 test diff --git a/hack/dev-push-conformance.sh b/hack/dev-push-conformance.sh index 5ed6fe1e5c313..2287785372302 100755 --- a/hack/dev-push-conformance.sh +++ b/hack/dev-push-conformance.sh @@ -41,9 +41,6 @@ fi IMAGE="${REGISTRY}/conformance-amd64:${VERSION}" kube::build::verify_prereqs -kube::build::build_image kube::build::run_build_command make WHAT="github.com/onsi/ginkgo/v2/ginkgo test/e2e/e2e.test cmd/kubectl test/conformance/image/go-runner" -kube::build::copy_output - make -C "${KUBE_ROOT}/test/conformance/image" build docker push "${IMAGE}" diff --git a/hack/diff-protobuf.sh b/hack/diff-protobuf.sh new file mode 100755 index 0000000000000..77dc503d14725 --- /dev/null +++ b/hack/diff-protobuf.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright 2025 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. + +if [[ $# -ne 2 ]]; then + echo "requires two arguments" + echo "" + echo "Examples:" + echo " hack/diff-protobuf.sh staging/src/k8s.io/api/testdata/v1.22.0/core.v1.CreateOptions{,.after_roundtrip}.pb" + echo "" + echo " git difftool -x hack/diff-protobuf.sh --no-prompt " + echo " git difftool -x hack/diff-protobuf.sh --no-prompt c0b7858946c253 4144c9294f7448 -- staging/src/k8s.io/api/testdata/HEAD/*.pb" + exit 1 +fi + +lhs="${1}" +rhs="${2}" +if [[ ! -f "${lhs}" && ! -f "${rhs}" ]]; then + echo "${lhs} and ${rhs} do not exist." + exit 1 +fi + +# simple case +diffcmd="diff" +if [ -t 1 ]; then + # if we're in a terminal, try to colorize + if diff --help | grep color >/dev/null; then + # diff supports --color + diffcmd="diff --color=always" + elif command -v colordiff &> /dev/null; then + # alternative color diff command + diffcmd="colordiff" + fi +fi + +echo "${BASE}" +if [[ "${lhs}" = *".pb" || "${rhs}" = *".pb" ]]; then + if ! command -v protoc &> /dev/null + then + echo "protoc command not found" + exit 1 + fi + + $diffcmd -u \ + <(tail -c +5 "${lhs}" | protoc --decode_raw) \ + <(tail -c +5 "${rhs}" | protoc --decode_raw) \ + | tail -n +3 +else + $diffcmd -u "${lhs}" "${rhs}" | tail -n +3 +fi +echo "" diff --git a/hack/ginkgo-e2e.sh b/hack/ginkgo-e2e.sh index b0fc78e6bca1b..1a4f97f18ab02 100755 --- a/hack/ginkgo-e2e.sh +++ b/hack/ginkgo-e2e.sh @@ -181,7 +181,16 @@ case "${E2E_TEST_DEBUG_TOOL:-ginkgo}" in if [[ -n "${GINKGO_PARALLEL_NODES:-}" ]]; then program+=("--nodes=${GINKGO_PARALLEL_NODES}") elif [[ ${GINKGO_PARALLEL} =~ ^[yY]$ ]]; then - program+=("--nodes=25") + if [[ "${KUBE_RACE:-}" == "-race" ]]; then + # When race detection is enabled, merely running 25 e2e.test instances was too much + # and the OOM killer shut down the Prow test pod because of the memory overhead. + # + # A CI job could control that via GINKGO_PARALLEL_NODES, but we should also have + # saner defaults which take this into account. + program+=("--nodes=10") + else + program+=("--nodes=25") + fi fi program+=("${ginkgo_args[@]:+${ginkgo_args[@]}}") ;; diff --git a/hack/golangci-hints.yaml b/hack/golangci-hints.yaml index 492f22d182174..5d54b511c5c86 100644 --- a/hack/golangci-hints.yaml +++ b/hack/golangci-hints.yaml @@ -124,6 +124,44 @@ linters: # Exceptions for kube-api-linter. # Exceptions are used for kube-api-linter to ignore existing issues that cannot be fixed without breaking changes. + + # Pre-existing issues from the conditions linter + + # Conditions generally should be a metav1.Condition, and should not use custom condition types. + - text: "Conditions field in StorageVersionStatus|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StorageVersionMigrationStatus must be a slice of metav1.Condition" + path: "staging/src/k8s.io/api/" + + # Conditions should have patch strategy markers, but changing these after shipping a client is a breaking change. + # Clients would treat these as atomic, when the patch strategy should be merge. + - text: "Conditions field in ValidatingAdmissionPolicyStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/admissionregistration/" + - text: "Conditions field in ValidatingAdmissionPolicyStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,3,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/admissionregistration/" + - text: "Conditions field in PodDisruptionBudgetStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/policy/" + - text: "Conditions field in AllocatedDeviceStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/resource/" + - text: "Conditions field in AllocatedDeviceStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,5,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/resource/" + + # Commentstart - Ignore commentstart issues for existing API group + # TODO: For each existing API group, we aim to remove it over time. + - text: "godoc for field .* should start with '.* ...'" + path: "staging/src/k8s.io/api/(admissionregistration|apidiscovery|apiserverinternal|apps|authentication|authorization|autoscaling|batch|certificates|coordination|core|discovery|events|extensions|flowcontrol|imagepolicy|networking|node|policy|rbac|resource|scheduling|storage|storagemigration)" + - text: "field .* is missing godoc comment" + path: "staging/src/k8s.io/api/autoscaling/" + + # Pre-existing issues from the conflictmarkers linter + # The Error field in some older API types is marked as both optional and required. + # This is incorrect, but cannot be changed without breaking changes. + - text: "field PortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/core/v1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/extensions/v1beta1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1beta1/types.go" default: standard enable: # please keep this alphabetized @@ -173,6 +211,7 @@ linters: structured k8s.io/kms/.* structured k8s.io/apiserver/pkg/storage/value/.* structured k8s.io/apiserver/pkg/server/options/encryptionconfig/.* + structured k8s.io/kubernetes/pkg/credentialprovider/plugin/.* # The following packages have been migrated to contextual logging. # Packages matched here do not have to be listed above because @@ -200,17 +239,35 @@ linters: contextual k8s.io/kubernetes/cmd/kube-proxy/.* contextual k8s.io/kubernetes/cmd/kube-scheduler/.* contextual k8s.io/kubernetes/cmd/kubelet/.* + contextual k8s.io/kubernetes/pkg/api/.* + contextual k8s.io/kubernetes/pkg/apis/.* + contextual k8s.io/kubernetes/pkg/capabilities/.* + contextual k8s.io/kubernetes/pkg/client/.* + contextual k8s.io/kubernetes/pkg/cluster/.* contextual k8s.io/kubernetes/pkg/controller/.* + contextual k8s.io/kubernetes/pkg/features/.* + contextual k8s.io/kubernetes/pkg/fieldpath/.* + contextual k8s.io/kubernetes/pkg/generated/.* + contextual k8s.io/kubernetes/pkg/printers/.* + contextual k8s.io/kubernetes/pkg/quota/.* contextual k8s.io/kubernetes/pkg/scheduler/.* + contextual k8s.io/kubernetes/pkg/security/.* + contextual k8s.io/kubernetes/pkg/securitycontext/.* contextual k8s.io/kubernetes/test/e2e/dra/.* + contextual k8s.io/kubernetes/pkg/kubelet/certificate/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/devicemanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/dra/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/.* contextual k8s.io/kubernetes/pkg/kubelet/lifecycle/.* contextual k8s.io/kubernetes/pkg/kubelet/pleg/.* contextual k8s.io/kubernetes/pkg/kubelet/clustertrustbundle/.* contextual k8s.io/kubernetes/pkg/kubelet/token/.* contextual k8s.io/kubernetes/pkg/kubelet/cadvisor/.* + contextual k8s.io/kubernetes/pkg/kubelet/config/.* contextual k8s.io/kubernetes/pkg/kubelet/oom/.* + contextual k8s.io/kubernetes/pkg/kubelet/server/.* contextual k8s.io/kubernetes/pkg/kubelet/status/.* contextual k8s.io/kubernetes/pkg/kubelet/sysctl/.* contextual k8s.io/kubernetes/pkg/kubelet/winstats/.* @@ -221,6 +278,9 @@ linters: contextual k8s.io/kubernetes/pkg/kubelet/pod/.* contextual k8s.io/kubernetes/pkg/kubelet/preemption/.* contextual k8s.io/kubernetes/pkg/kubelet/volumemanager/.* + contextual k8s.io/kubernetes/pkg/kubelet/util/.* + contextual k8s.io/kubernetes/pkg/kubelet/logs/.* + contextual k8s.io/kubernetes/test/images/sample-device-plugin/.* # As long as contextual logging is alpha or beta, all WithName, WithValues, # NewContext calls have to go through klog. Once it is GA, we can lift @@ -251,8 +311,9 @@ linters: disable: - '*' enable: - # - "commentstart" # Ensure comments start with the serialized version of the field name. - # - "conditions" # Ensure conditions have the correct json tags and markers. + - "commentstart" # Ensure comments start with the serialized version of the field name. + - "conditions" # Ensure conditions have the correct json tags and markers. + - "conflictingmarkers" # Detect mutually exclusive markers on the same field. # - "integers" # Ensure only int32 and int64 are used for integers. # - "jsontags" # Ensure every field has a json tag. # - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. ONLY for CRDs until declarative markers exist in core types. @@ -260,22 +321,61 @@ linters: # - "nofloats" # Ensure floats are not used. # - "nomaps" # Ensure maps are not used, unless they are `map[string]string` (for labels/annotations/etc). # - "nophase" # Ensure field names do not have the word "phase" in them. + # - "notimestamp" # Ensure fields are not named "timestamp", prefer "time". + # - "optionalfields" # Ensure fields marked optional have omitempty and pointers. # - "optionalorrequired" # Every field should be marked as `+optional` xor `+required`. - # - "requiredfields" # Required fields should not be pointers, and should not have `omitempty`. + # - "requiredfields" # Required fields should only be pointers when required based on the validity of the zero value, they should always have `omitempty`. + - "ssatags" # Ensure lists have a listType tag. + # - "uniquemarkers" # Ensure markers are not duplicated across field and type definitions. + - "duplicatemarkers" #Prevent identical markers from being present on types and fields lintersConfig: - # conditions: - # isFirstField: Warn # Require conditions to be the first field in the status struct. - # usePatchStrategy: SuggestFix # Conditions should not use the patch strategy on CRDs. - # useProtobuf: SuggestFix # We don't use protobuf, so protobuf tags are not required. + conditions: + isFirstField: Ignore + usePatchStrategy: SuggestFix + useProtobuf: SuggestFix + conflictingmarkers: + conflicts: + - name: "optional_vs_required" + sets: + - ["k8s:optional", "optional", "kubebuilder:validation:Optional"] + - ["k8s:required", "required", "kubebuilder:validation:Required"] + description: "fields cannot be both optional and required" + - name: "required_vs_default" + sets: + - ["k8s:required", "required", "kubebuilder:validation:Required"] + - ["default"] + description: "fields with default values are always optional" # jsonTags: # jsonTagRegex: "^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$" # The default regex is appropriate for our use case. # nomaps: # policy: AllowStringToStringMaps # Determines how the linter should handle maps of basic types. Maps of objects are always disallowed. + # optionalFields: + # policy: AllowOptionalFields # Determines how the linter should handle optional fields.optionalfields: + # pointers: + # preference: Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`. + # policy: SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in optional fields. Defaults to `SuggestFix`. # optionalOrRequired: # preferredOptionalMarker: optional # The preferred optional marker to use, fixes will suggest to use this marker. Defaults to `optional`. - # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # policy: AllowOptionalFields # Determines how the linter should handle optional fields. # requiredFields: - # pointerPolicy: SuggestFix # Defaults to `SuggestFix`. We want our required fields to not be pointers. + # pointers: + # policy: SuggestFix | Warn # The policy for pointers in required fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in required fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in required fields. Defaults to `SuggestFix`. + ssatags: + listTypeSetUsage: Ignore # The policy for listType=set usage on object arrays. Defaults to `Warn`. + # uniquemarkers: + # customMarkers: + # - identifier: custom:SomeCustomMarker + # attributes: + # - fruit depguard: rules: go-cmp: @@ -293,6 +393,9 @@ linters: forbidigo: analyze-types: true forbid: + - pattern: md5\.* + # https://github.com/kubernetes/kubernetes/issues/129652 + msg: md5 is oudated, insecure, prefer a secure hash (such as sha256) or a non-cryptographic hash - pattern: ^managedfields\.ExtractInto$ pkg: ^k8s\.io/apimachinery/pkg/util/managedfields$ msg: should not be used because managedFields was removed @@ -317,5 +420,6 @@ linters: staticcheck: checks: - "all" + - "-QF1008" # Omit embedded fields from selector expression testifylint: enable-all: true diff --git a/hack/golangci.yaml b/hack/golangci.yaml index 4c1725055e1cc..fa853d4afa5c3 100644 --- a/hack/golangci.yaml +++ b/hack/golangci.yaml @@ -126,12 +126,7 @@ linters: - linters: - gocritic - text: "append result not assigned to the same slice|put a space between `//` and comment text|sloppyLen|elseif|should rewrite switch statement to if statement|regexpMust|wrapperFunc: use strings.ReplaceAll|singleCaseSwitch|deprecatedComment|exitAfterDefer|captLocal|unlambda|underef|unslice|valSwap|typeSwitchVar" - - # https://github.com/kubernetes/kubernetes/issues/117288#issuecomment-1507008918 - - linters: - - gocritic - text: "assignOp:" + text: "wrapperFunc: use strings.ReplaceAll|should rewrite switch statement to if statement" # Kube-API-Linter should only be run on the API definitions - linters: @@ -140,6 +135,44 @@ linters: # Exceptions for kube-api-linter. # Exceptions are used for kube-api-linter to ignore existing issues that cannot be fixed without breaking changes. + + # Pre-existing issues from the conditions linter + + # Conditions generally should be a metav1.Condition, and should not use custom condition types. + - text: "Conditions field in StorageVersionStatus|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StorageVersionMigrationStatus must be a slice of metav1.Condition" + path: "staging/src/k8s.io/api/" + + # Conditions should have patch strategy markers, but changing these after shipping a client is a breaking change. + # Clients would treat these as atomic, when the patch strategy should be merge. + - text: "Conditions field in ValidatingAdmissionPolicyStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/admissionregistration/" + - text: "Conditions field in ValidatingAdmissionPolicyStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,3,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/admissionregistration/" + - text: "Conditions field in PodDisruptionBudgetStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/policy/" + - text: "Conditions field in AllocatedDeviceStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/resource/" + - text: "Conditions field in AllocatedDeviceStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,5,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/resource/" + + # Commentstart - Ignore commentstart issues for existing API group + # TODO: For each existing API group, we aim to remove it over time. + - text: "godoc for field .* should start with '.* ...'" + path: "staging/src/k8s.io/api/(admissionregistration|apidiscovery|apiserverinternal|apps|authentication|authorization|autoscaling|batch|certificates|coordination|core|discovery|events|extensions|flowcontrol|imagepolicy|networking|node|policy|rbac|resource|scheduling|storage|storagemigration)" + - text: "field .* is missing godoc comment" + path: "staging/src/k8s.io/api/autoscaling/" + + # Pre-existing issues from the conflictmarkers linter + # The Error field in some older API types is marked as both optional and required. + # This is incorrect, but cannot be changed without breaking changes. + - text: "field PortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/core/v1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/extensions/v1beta1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1/types.go" + - text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1beta1/types.go" default: none enable: # please keep this alphabetized @@ -187,6 +220,7 @@ linters: structured k8s.io/kms/.* structured k8s.io/apiserver/pkg/storage/value/.* structured k8s.io/apiserver/pkg/server/options/encryptionconfig/.* + structured k8s.io/kubernetes/pkg/credentialprovider/plugin/.* # The following packages have been migrated to contextual logging. # Packages matched here do not have to be listed above because @@ -214,17 +248,35 @@ linters: contextual k8s.io/kubernetes/cmd/kube-proxy/.* contextual k8s.io/kubernetes/cmd/kube-scheduler/.* contextual k8s.io/kubernetes/cmd/kubelet/.* + contextual k8s.io/kubernetes/pkg/api/.* + contextual k8s.io/kubernetes/pkg/apis/.* + contextual k8s.io/kubernetes/pkg/capabilities/.* + contextual k8s.io/kubernetes/pkg/client/.* + contextual k8s.io/kubernetes/pkg/cluster/.* contextual k8s.io/kubernetes/pkg/controller/.* + contextual k8s.io/kubernetes/pkg/features/.* + contextual k8s.io/kubernetes/pkg/fieldpath/.* + contextual k8s.io/kubernetes/pkg/generated/.* + contextual k8s.io/kubernetes/pkg/printers/.* + contextual k8s.io/kubernetes/pkg/quota/.* contextual k8s.io/kubernetes/pkg/scheduler/.* + contextual k8s.io/kubernetes/pkg/security/.* + contextual k8s.io/kubernetes/pkg/securitycontext/.* contextual k8s.io/kubernetes/test/e2e/dra/.* + contextual k8s.io/kubernetes/pkg/kubelet/certificate/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/devicemanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/dra/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/.* + contextual k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/.* contextual k8s.io/kubernetes/pkg/kubelet/lifecycle/.* contextual k8s.io/kubernetes/pkg/kubelet/pleg/.* contextual k8s.io/kubernetes/pkg/kubelet/clustertrustbundle/.* contextual k8s.io/kubernetes/pkg/kubelet/token/.* contextual k8s.io/kubernetes/pkg/kubelet/cadvisor/.* + contextual k8s.io/kubernetes/pkg/kubelet/config/.* contextual k8s.io/kubernetes/pkg/kubelet/oom/.* + contextual k8s.io/kubernetes/pkg/kubelet/server/.* contextual k8s.io/kubernetes/pkg/kubelet/status/.* contextual k8s.io/kubernetes/pkg/kubelet/sysctl/.* contextual k8s.io/kubernetes/pkg/kubelet/winstats/.* @@ -235,6 +287,9 @@ linters: contextual k8s.io/kubernetes/pkg/kubelet/pod/.* contextual k8s.io/kubernetes/pkg/kubelet/preemption/.* contextual k8s.io/kubernetes/pkg/kubelet/volumemanager/.* + contextual k8s.io/kubernetes/pkg/kubelet/util/.* + contextual k8s.io/kubernetes/pkg/kubelet/logs/.* + contextual k8s.io/kubernetes/test/images/sample-device-plugin/.* # As long as contextual logging is alpha or beta, all WithName, WithValues, # NewContext calls have to go through klog. Once it is GA, we can lift @@ -265,8 +320,9 @@ linters: disable: - '*' enable: - # - "commentstart" # Ensure comments start with the serialized version of the field name. - # - "conditions" # Ensure conditions have the correct json tags and markers. + - "commentstart" # Ensure comments start with the serialized version of the field name. + - "conditions" # Ensure conditions have the correct json tags and markers. + - "conflictingmarkers" # Detect mutually exclusive markers on the same field. # - "integers" # Ensure only int32 and int64 are used for integers. # - "jsontags" # Ensure every field has a json tag. # - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. ONLY for CRDs until declarative markers exist in core types. @@ -274,22 +330,61 @@ linters: # - "nofloats" # Ensure floats are not used. # - "nomaps" # Ensure maps are not used, unless they are `map[string]string` (for labels/annotations/etc). # - "nophase" # Ensure field names do not have the word "phase" in them. + # - "notimestamp" # Ensure fields are not named "timestamp", prefer "time". + # - "optionalfields" # Ensure fields marked optional have omitempty and pointers. # - "optionalorrequired" # Every field should be marked as `+optional` xor `+required`. - # - "requiredfields" # Required fields should not be pointers, and should not have `omitempty`. + # - "requiredfields" # Required fields should only be pointers when required based on the validity of the zero value, they should always have `omitempty`. + - "ssatags" # Ensure lists have a listType tag. + # - "uniquemarkers" # Ensure markers are not duplicated across field and type definitions. + - "duplicatemarkers" #Prevent identical markers from being present on types and fields lintersConfig: - # conditions: - # isFirstField: Warn # Require conditions to be the first field in the status struct. - # usePatchStrategy: SuggestFix # Conditions should not use the patch strategy on CRDs. - # useProtobuf: SuggestFix # We don't use protobuf, so protobuf tags are not required. + conditions: + isFirstField: Ignore + usePatchStrategy: SuggestFix + useProtobuf: SuggestFix + conflictingmarkers: + conflicts: + - name: "optional_vs_required" + sets: + - ["k8s:optional", "optional", "kubebuilder:validation:Optional"] + - ["k8s:required", "required", "kubebuilder:validation:Required"] + description: "fields cannot be both optional and required" + - name: "required_vs_default" + sets: + - ["k8s:required", "required", "kubebuilder:validation:Required"] + - ["default"] + description: "fields with default values are always optional" # jsonTags: # jsonTagRegex: "^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$" # The default regex is appropriate for our use case. # nomaps: # policy: AllowStringToStringMaps # Determines how the linter should handle maps of basic types. Maps of objects are always disallowed. + # optionalFields: + # policy: AllowOptionalFields # Determines how the linter should handle optional fields.optionalfields: + # pointers: + # preference: Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`. + # policy: SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in optional fields. Defaults to `SuggestFix`. # optionalOrRequired: # preferredOptionalMarker: optional # The preferred optional marker to use, fixes will suggest to use this marker. Defaults to `optional`. - # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # policy: AllowOptionalFields # Determines how the linter should handle optional fields. # requiredFields: - # pointerPolicy: SuggestFix # Defaults to `SuggestFix`. We want our required fields to not be pointers. + # pointers: + # policy: SuggestFix | Warn # The policy for pointers in required fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in required fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in required fields. Defaults to `SuggestFix`. + ssatags: + listTypeSetUsage: Ignore # The policy for listType=set usage on object arrays. Defaults to `Warn`. + # uniquemarkers: + # customMarkers: + # - identifier: custom:SomeCustomMarker + # attributes: + # - fruit depguard: rules: go-cmp: @@ -307,6 +402,9 @@ linters: forbidigo: analyze-types: true forbid: + - pattern: md5\.* + # https://github.com/kubernetes/kubernetes/issues/129652 + msg: md5 is oudated, insecure, prefer a secure hash (such as sha256) or a non-cryptographic hash - pattern: ^managedfields\.ExtractInto$ pkg: ^k8s\.io/apimachinery/pkg/util/managedfields$ msg: should not be used because managedFields was removed @@ -316,10 +414,25 @@ linters: - pattern: \.Add$ pkg: ^k8s\.io/component-base/featuregate$ msg: should not use Add, use AddVersioned instead - gocritic: - enabled-checks: - - equalFold + gocritic: + enabled-checks: # These are in addition to the default checks - see https://golangci-lint.run/docs/linters/configuration/#gocritic - boolExprSimplify + - equalFold + disabled-checks: # This disables checks that are enabled by default (this can be combined with enabled-checks, contrary to the docs) + - appendAssign + - assignOp # https://github.com/kubernetes/kubernetes/issues/117288#issuecomment-1507008918 + - captLocal + - commentFormatting + - deprecatedComment + - elseif + # The following have few (single-digit) occurrences left in k/k and wouldn't be too onerous to enable + - exitAfterDefer + - regexpMust + - sloppyLen + - typeSwitchVar + - underef + - unslice + - valSwap revive: # Only these rules are enabled. rules: @@ -329,6 +442,7 @@ linters: staticcheck: checks: - "all" + - "-QF1008" # Omit embedded fields from selector expression - "-QF1001" # Apply De Morgan’s law - "-QF1002" # Convert untagged switch to tagged switch - "-QF1003" # Convert if/else-if chain to tagged switch diff --git a/hack/golangci.yaml.in b/hack/golangci.yaml.in index 0b6797cdcdb1c..13da763a9a8f3 100644 --- a/hack/golangci.yaml.in +++ b/hack/golangci.yaml.in @@ -141,12 +141,7 @@ linters: - linters: - gocritic - text: "append result not assigned to the same slice|put a space between `//` and comment text|sloppyLen|elseif|should rewrite switch statement to if statement|regexpMust|wrapperFunc: use strings.ReplaceAll|singleCaseSwitch|deprecatedComment|exitAfterDefer|captLocal|unlambda|underef|unslice|valSwap|typeSwitchVar" - - # https://github.com/kubernetes/kubernetes/issues/117288#issuecomment-1507008918 - - linters: - - gocritic - text: "assignOp:" + text: "wrapperFunc: use strings.ReplaceAll|should rewrite switch statement to if statement" {{- end}} @@ -219,6 +214,9 @@ linters: forbidigo: analyze-types: true forbid: + - pattern: md5\.* + # https://github.com/kubernetes/kubernetes/issues/129652 + msg: md5 is oudated, insecure, prefer a secure hash (such as sha256) or a non-cryptographic hash - pattern: ^managedfields\.ExtractInto$ pkg: ^k8s\.io/apimachinery/pkg/util/managedfields$ msg: should not be used because managedFields was removed @@ -237,10 +235,25 @@ linters: msg: "it does not produce a good failure message - use BeFalseBecause with an explicit printf-style failure message instead, or plain Go: if ... { ginkgo.Fail(...) }" {{- end}} {{- if .Base }} - gocritic: - enabled-checks: - - equalFold - - boolExprSimplify + gocritic: + enabled-checks: # These are in addition to the default checks - see https://golangci-lint.run/docs/linters/configuration/#gocritic + - boolExprSimplify + - equalFold + disabled-checks: # This disables checks that are enabled by default (this can be combined with enabled-checks, contrary to the docs) + - appendAssign + - assignOp # https://github.com/kubernetes/kubernetes/issues/117288#issuecomment-1507008918 + - captLocal + - commentFormatting + - deprecatedComment + - elseif + # The following have few (single-digit) occurrences left in k/k and wouldn't be too onerous to enable + - exitAfterDefer + - regexpMust + - sloppyLen + - typeSwitchVar + - underef + - unslice + - valSwap {{- end}} revive: # Only these rules are enabled. @@ -251,6 +264,7 @@ linters: staticcheck: checks: - "all" + - "-QF1008" # Omit embedded fields from selector expression {{- if .Base }} - "-QF1001" # Apply De Morgan’s law - "-QF1002" # Convert untagged switch to tagged switch diff --git a/hack/jenkins/test-cmd-dockerized.sh b/hack/jenkins/test-cmd-dockerized.sh index 1dcb47a503c74..78792a574ecbb 100755 --- a/hack/jenkins/test-cmd-dockerized.sh +++ b/hack/jenkins/test-cmd-dockerized.sh @@ -17,7 +17,6 @@ set -o errexit set -o nounset set -o pipefail -set -o xtrace # Runs test-cmd, intended to be run in prow.k8s.io diff --git a/hack/jenkins/test-dockerized.sh b/hack/jenkins/test-dockerized.sh index 7a0c527beb29b..d118b3b3fdb42 100755 --- a/hack/jenkins/test-dockerized.sh +++ b/hack/jenkins/test-dockerized.sh @@ -17,7 +17,6 @@ set -o errexit set -o nounset set -o pipefail -set -o xtrace # Runs test-cmd and test-integration, intended to be run in prow.k8s.io diff --git a/hack/jenkins/test-integration-dockerized.sh b/hack/jenkins/test-integration-dockerized.sh index fe8c558ccece8..25f6f1b5fde44 100755 --- a/hack/jenkins/test-integration-dockerized.sh +++ b/hack/jenkins/test-integration-dockerized.sh @@ -17,7 +17,6 @@ set -o errexit set -o nounset set -o pipefail -set -o xtrace # Runs test-integration # This script is intended to be run from prow.k8s.io diff --git a/hack/kube-api-linter/exceptions.yaml b/hack/kube-api-linter/exceptions.yaml index 301d5f6738307..710819836f532 100644 --- a/hack/kube-api-linter/exceptions.yaml +++ b/hack/kube-api-linter/exceptions.yaml @@ -1,2 +1,40 @@ # Exceptions for kube-api-linter. # Exceptions are used for kube-api-linter to ignore existing issues that cannot be fixed without breaking changes. + +# Pre-existing issues from the conditions linter + +# Conditions generally should be a metav1.Condition, and should not use custom condition types. +- text: "Conditions field in StorageVersionStatus|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StatefulSetStatus|DeploymentStatus|DaemonSetStatus|ReplicaSetStatus|HorizontalPodAutoscalerStatus|JobStatus|CertificateSigningRequestStatus|PersistentVolumeClaimStatus|ReplicationControllerStatus|ServiceStatus|NodeStatus|NamespaceStatus|ComponentStatus|PodStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|FlowSchemaStatus|PriorityLevelConfigurationStatus|PodDisruptionBudgetStatus|AllocatedDeviceStatus|Endpoint|StorageVersionMigrationStatus must be a slice of metav1.Condition" + path: "staging/src/k8s.io/api/" + +# Conditions should have patch strategy markers, but changing these after shipping a client is a breaking change. +# Clients would treat these as atomic, when the patch strategy should be merge. +- text: "Conditions field in ValidatingAdmissionPolicyStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/admissionregistration/" +- text: "Conditions field in ValidatingAdmissionPolicyStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,3,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/admissionregistration/" +- text: "Conditions field in PodDisruptionBudgetStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/policy/" +- text: "Conditions field in AllocatedDeviceStatus is missing the following markers: patchStrategy=merge, patchMergeKey=type" + path: "staging/src/k8s.io/api/resource/" +- text: "Conditions field in AllocatedDeviceStatus has incorrect tags, should be: `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,5,rep,name=conditions\"`" + path: "staging/src/k8s.io/api/resource/" + +# Commentstart - Ignore commentstart issues for existing API group +# TODO: For each existing API group, we aim to remove it over time. +- text: "godoc for field .* should start with '.* ...'" + path: "staging/src/k8s.io/api/(admissionregistration|apidiscovery|apiserverinternal|apps|authentication|authorization|autoscaling|batch|certificates|coordination|core|discovery|events|extensions|flowcontrol|imagepolicy|networking|node|policy|rbac|resource|scheduling|storage|storagemigration)" +- text: "field .* is missing godoc comment" + path: "staging/src/k8s.io/api/autoscaling/" + +# Pre-existing issues from the conflictmarkers linter +# The Error field in some older API types is marked as both optional and required. +# This is incorrect, but cannot be changed without breaking changes. +- text: "field PortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/core/v1/types.go" +- text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/extensions/v1beta1/types.go" +- text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1/types.go" +- text: "field IngressPortStatus.Error has conflicting markers: optional_vs_required: {\\[optional\\], \\[kubebuilder:validation:Required\\]}. fields cannot be both optional and required" + path: "staging/src/k8s.io/api/networking/v1beta1/types.go" diff --git a/hack/kube-api-linter/kube-api-linter.yaml b/hack/kube-api-linter/kube-api-linter.yaml index 2ad2fbdcd1237..0a6c0f839df7c 100644 --- a/hack/kube-api-linter/kube-api-linter.yaml +++ b/hack/kube-api-linter/kube-api-linter.yaml @@ -3,8 +3,9 @@ linters: disable: - '*' enable: - # - "commentstart" # Ensure comments start with the serialized version of the field name. - # - "conditions" # Ensure conditions have the correct json tags and markers. + - "commentstart" # Ensure comments start with the serialized version of the field name. + - "conditions" # Ensure conditions have the correct json tags and markers. + - "conflictingmarkers" # Detect mutually exclusive markers on the same field. # - "integers" # Ensure only int32 and int64 are used for integers. # - "jsontags" # Ensure every field has a json tag. # - "maxlength" # Ensure all strings and arrays have maximum lengths/maximum items. ONLY for CRDs until declarative markers exist in core types. @@ -12,19 +13,58 @@ linters: # - "nofloats" # Ensure floats are not used. # - "nomaps" # Ensure maps are not used, unless they are `map[string]string` (for labels/annotations/etc). # - "nophase" # Ensure field names do not have the word "phase" in them. + # - "notimestamp" # Ensure fields are not named "timestamp", prefer "time". + # - "optionalfields" # Ensure fields marked optional have omitempty and pointers. # - "optionalorrequired" # Every field should be marked as `+optional` xor `+required`. - # - "requiredfields" # Required fields should not be pointers, and should not have `omitempty`. + # - "requiredfields" # Required fields should only be pointers when required based on the validity of the zero value, they should always have `omitempty`. + - "ssatags" # Ensure lists have a listType tag. + # - "uniquemarkers" # Ensure markers are not duplicated across field and type definitions. + - "duplicatemarkers" #Prevent identical markers from being present on types and fields lintersConfig: - # conditions: - # isFirstField: Warn # Require conditions to be the first field in the status struct. - # usePatchStrategy: SuggestFix # Conditions should not use the patch strategy on CRDs. - # useProtobuf: SuggestFix # We don't use protobuf, so protobuf tags are not required. + conditions: + isFirstField: Ignore + usePatchStrategy: SuggestFix + useProtobuf: SuggestFix + conflictingmarkers: + conflicts: + - name: "optional_vs_required" + sets: + - ["k8s:optional", "optional", "kubebuilder:validation:Optional"] + - ["k8s:required", "required", "kubebuilder:validation:Required"] + description: "fields cannot be both optional and required" + - name: "required_vs_default" + sets: + - ["k8s:required", "required", "kubebuilder:validation:Required"] + - ["default"] + description: "fields with default values are always optional" # jsonTags: # jsonTagRegex: "^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$" # The default regex is appropriate for our use case. # nomaps: # policy: AllowStringToStringMaps # Determines how the linter should handle maps of basic types. Maps of objects are always disallowed. + # optionalFields: + # policy: AllowOptionalFields # Determines how the linter should handle optional fields.optionalfields: + # pointers: + # preference: Always | WhenRequired # Whether to always require pointers, or only when required. Defaults to `Always`. + # policy: SuggestFix | Warn # The policy for pointers in optional fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in optional fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in optional fields. Defaults to `SuggestFix`. # optionalOrRequired: # preferredOptionalMarker: optional # The preferred optional marker to use, fixes will suggest to use this marker. Defaults to `optional`. - # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # preferredRequiredMarker: required # The preferred required marker to use, fixes will suggest to use this marker. Defaults to `required`. + # policy: AllowOptionalFields # Determines how the linter should handle optional fields. # requiredFields: - # pointerPolicy: SuggestFix # Defaults to `SuggestFix`. We want our required fields to not be pointers. + # pointers: + # policy: SuggestFix | Warn # The policy for pointers in required fields. Defaults to `SuggestFix`. + # omitempty: + # policy: SuggestFix | Warn | Ignore # The policy for omitempty in required fields. Defaults to `SuggestFix`. + # omitzero: + # policy: SuggestFix | Warn | Forbid # The policy for omitzero in required fields. Defaults to `SuggestFix`. + ssatags: + listTypeSetUsage: Ignore # The policy for listType=set usage on object arrays. Defaults to `Warn`. + # uniquemarkers: + # customMarkers: + # - identifier: custom:SomeCustomMarker + # attributes: + # - fruit diff --git a/hack/lib/etcd.sh b/hack/lib/etcd.sh index 6e99d244c808b..d97de5206aa3f 100755 --- a/hack/lib/etcd.sh +++ b/hack/lib/etcd.sh @@ -16,7 +16,7 @@ # A set of helpers for starting/running etcd for tests -ETCD_VERSION=${ETCD_VERSION:-3.6.5} +ETCD_VERSION=${ETCD_VERSION:-3.6.6} ETCD_HOST=${ETCD_HOST:-127.0.0.1} ETCD_PORT=${ETCD_PORT:-2379} # This is intentionally not called ETCD_LOG_LEVEL: diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh index e9992e72eeb5b..c401de05800c2 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh @@ -519,7 +519,7 @@ EOF local go_version IFS=" " read -ra go_version <<< "$(GOFLAGS='' go version)" local minimum_go_version - minimum_go_version=go1.24 + minimum_go_version=go1.25 if [[ "${minimum_go_version}" != $(echo -e "${minimum_go_version}\n${go_version[2]}" | sort -s -t. -k 1,1 -k 2,2n -k 3,3n | head -n1) && "${go_version[2]}" != "devel" ]]; then kube::log::usage_from_stdin < 0 to enable rsync -# compression for build container -KUBE_RSYNC_COMPRESS="${KUBE_RSYNC_COMPRESS:-0}" - -# Set no_proxy for localhost if behind a proxy, otherwise, -# the connections to localhost in scripts will time out -export no_proxy="127.0.0.1,localhost${no_proxy:+,${no_proxy}}" - source "${KUBE_ROOT}/hack/lib/util.sh" source "${KUBE_ROOT}/hack/lib/logging.sh" @@ -119,7 +112,7 @@ storage.k8s.io/v1beta1 \ storage.k8s.io/v1 \ storage.k8s.io/v1alpha1 \ flowcontrol.apiserver.k8s.io/v1 \ -storagemigration.k8s.io/v1alpha1 \ +storagemigration.k8s.io/v1beta1 \ flowcontrol.apiserver.k8s.io/v1beta1 \ flowcontrol.apiserver.k8s.io/v1beta2 \ flowcontrol.apiserver.k8s.io/v1beta3 \ diff --git a/hack/lib/logging.sh b/hack/lib/logging.sh index 15a286cea7850..4b6428232a3e4 100644 --- a/hack/lib/logging.sh +++ b/hack/lib/logging.sh @@ -169,3 +169,13 @@ kube::log::status() { echo " ${message}" done } + +# Log a command and run it. Uses a subshell which gets replaced by the command after logging. +kube::log::run() ( + V="${V:-0}" + if (( KUBE_VERBOSE >= V )); then + timestamp=$(date +"[%m%d %H:%M:%S]") + echo "+++ ${timestamp} ${*}" + fi + exec "${@}" +) diff --git a/hack/lib/protoc.sh b/hack/lib/protoc.sh index c260ec011fa5a..a23dfffe0f5ce 100644 --- a/hack/lib/protoc.sh +++ b/hack/lib/protoc.sh @@ -42,20 +42,6 @@ function kube::protoc::generate_proto() { kube::protoc::format "${package}" } -# Generates $1/api.pb.go from the protobuf file $1/api.proto -# and formats it correctly -# $1: Full path to the directory where the api.proto file is -function kube::protoc::generate_proto_gogo() { - kube::golang::setup_env - GOPROXY=off go install k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo - - kube::protoc::check_protoc - - local package=${1} - kube::protoc::protoc_gogo "${package}" - kube::protoc::format "${package}" -} - # Checks that the current protoc version matches the required version and # exit 1 if it's not the case function kube::protoc::check_protoc() { @@ -70,29 +56,6 @@ function kube::protoc::check_protoc() { # Generates $1/api.pb.go from the protobuf file $1/api.proto # $1: Full path to the directory where the api.proto file is -function kube::protoc::protoc_gogo() { - local package=${1} - gogopath=$(dirname "$(kube::util::find-binary "protoc-gen-gogo")") - - ( - cd "${package}" - - # This invocation of --gogo_out produces its output in the current - # directory (despite gogo docs saying it would be source-relative, it - # isn't). The inputs to this function do not all have a common root, so - # this works best for all inputs. - PATH="${gogopath}:${PATH}" protoc \ - --proto_path="$(pwd -P)" \ - --proto_path="${KUBE_ROOT}/vendor" \ - --proto_path="${KUBE_ROOT}/staging/src" \ - --proto_path="${KUBE_ROOT}/third_party/protobuf" \ - --gogo_out=paths=source_relative,plugins=grpc:. \ - api.proto - ) -} - -# Generates $1/api.pb.go from the protobuf file $1/api.proto without using gogo -# $1: Full path to the directory where the api.proto file is function kube::protoc::protoc() { local package=${1} diff --git a/hack/lib/util.sh b/hack/lib/util.sh index cae8a499204d1..ed067cc7a30ba 100755 --- a/hack/lib/util.sh +++ b/hack/lib/util.sh @@ -720,22 +720,22 @@ function kube::util::ensure-gnu-sed { kube::util::sourced_variable "${SED}" } -# kube::util::ensure-gnu-date +# kube::util::ensure-gnu-compatible-date # Determines which date binary is gnu-date on linux/darwin # # Sets: # DATE: The name of the gnu-date binary # -function kube::util::ensure-gnu-date { +function kube::util::ensure-gnu-compatible-date { # NOTE: the echo below is a workaround to ensure date is executed before the grep. # see: https://github.com/kubernetes/kubernetes/issues/87251 - date_help="$(LANG=C date --help 2>&1 || true)" - if echo "${date_help}" | grep -q "GNU\|BusyBox"; then + date_version="$(LANG=C date --version 2>&1 || true)" + if echo "${date_version}" | grep -q "GNU\|BusyBox\|uutils"; then DATE="date" elif command -v gdate &>/dev/null; then DATE="gdate" else - kube::log::error "Failed to find GNU date as date or gdate. If you are on Mac: brew install coreutils." >&2 + kube::log::error "Failed to find GNU-compatible date as date or gdate. If you are on Mac: brew install coreutils." >&2 return 1 fi kube::util::sourced_variable "${DATE}" diff --git a/hack/lib/verify-generated.sh b/hack/lib/verify-generated.sh index 4770f99ca5c47..c795c15a5149a 100755 --- a/hack/lib/verify-generated.sh +++ b/hack/lib/verify-generated.sh @@ -39,7 +39,7 @@ kube::verify::generated() { _tmpdir="$(kube::realpath "$(mktemp -d -t "verify-generated-$(basename "$1").XXXXXX")")" git worktree add -f -q "${_tmpdir}" HEAD - kube::util::trap_add "git worktree remove -f ${_tmpdir}" EXIT + kube::util::trap_add "git worktree remove -f ${_tmpdir:?}; rm -rf ${_tmpdir:?}" EXIT cd "${_tmpdir}" # Update generated files. diff --git a/hack/lib/version.sh b/hack/lib/version.sh index ae3853df3841a..9b6efaaff490f 100644 --- a/hack/lib/version.sh +++ b/hack/lib/version.sh @@ -163,7 +163,7 @@ kube::version::ldflags() { ) } - kube::util::ensure-gnu-date + kube::util::ensure-gnu-compatible-date add_ldflag "buildDate" "$(${DATE} ${SOURCE_DATE_EPOCH:+"--date=@${SOURCE_DATE_EPOCH}"} -u +'%Y-%m-%dT%H:%M:%SZ')" if [[ -n ${KUBE_GIT_COMMIT-} ]]; then diff --git a/hack/local-up-cluster.sh b/hack/local-up-cluster.sh index fe6616f69ed0a..46491b223ff19 100755 --- a/hack/local-up-cluster.sh +++ b/hack/local-up-cluster.sh @@ -59,7 +59,7 @@ LIMITED_SWAP=${LIMITED_SWAP:-""} # required for cni installation CNI_CONFIG_DIR=${CNI_CONFIG_DIR:-/etc/cni/net.d} -CNI_PLUGINS_VERSION=${CNI_PLUGINS_VERSION:-"v1.7.1"} +CNI_PLUGINS_VERSION=${CNI_PLUGINS_VERSION:-"v1.8.0"} # The arch of the CNI binary, if not set, will be fetched based on the value of `uname -m` CNI_TARGETARCH=${CNI_TARGETARCH:-""} CNI_PLUGINS_URL="https://github.com/containernetworking/plugins/releases/download" @@ -670,6 +670,7 @@ EOF --egress-selector-config-file="${EGRESS_SELECTOR_CONFIG_FILE:-}" \ --client-ca-file="${CERT_DIR}/client-ca.crt" \ --kubelet-client-certificate="${CERT_DIR}/client-kube-apiserver.crt" \ + --kubelet-certificate-authority="${CLUSTER_SIGNING_CERT_FILE}" \ --kubelet-client-key="${CERT_DIR}/client-kube-apiserver.key" \ --kubelet-port="${KUBELET_PORT}" \ --kubelet-read-only-port="${KUBELET_READ_ONLY_PORT}" \ @@ -794,6 +795,20 @@ function start_cloud_controller_manager { export CLOUD_CTLRMGR_PID=$! } +function wait_node_csr() { + local interval_time=2 + local csr_approved_time=300 + local newline='"\n"' + local unapproved_csr_names="--field-selector='spec.signerName=kubernetes.io/kubelet-serving' -o go-template='{{range .items}}{{if not .status}}{{.metadata.name}}{{${newline}}}{{end}}{{end}}" + local csr_approved="${KUBECTL} --kubeconfig '${CERT_DIR}/admin.kubeconfig' get csr ${unapproved_csr_names}' | xargs --no-run-if-empty ${KUBECTL} --kubeconfig '${CERT_DIR}/admin.kubeconfig' certificate approve | grep csr" + kube::util::wait_for_success "$csr_approved_time" "$interval_time" "$csr_approved" + if [ $? == "1" ]; then + echo "time out on waiting for CSR approval" + exit 1 + fi + echo "kubelet CSR approved" +} + function wait_node_ready(){ if [[ -n "${DRY_RUN}" ]]; then return @@ -923,6 +938,7 @@ readOnlyPort: ${KUBELET_READ_ONLY_PORT} healthzPort: ${KUBELET_HEALTHZ_PORT} healthzBindAddress: ${KUBELET_HOST} rotateCertificates: true +serverTLSBootstrap: true runtimeRequestTimeout: "${RUNTIME_REQUEST_TIMEOUT}" staticPodPath: "${POD_MANIFEST_PATH}" resolvConf: "${KUBELET_RESOLV_CONF}" @@ -1388,8 +1404,8 @@ if [[ "${KUBETEST_IN_DOCKER:-}" == "true" ]]; then # configure and start containerd echo "configuring containerd" containerd config default > /etc/containerd/config.toml - sed -ie 's|root = "/var/lib/containerd"|root = "/docker-graph/containerd/daemon"|' /etc/containerd/config.toml - sed -ie 's|state = "/run/containerd"|state = "/var/run/docker/containerd/daemon"|' /etc/containerd/config.toml + sed -ie 's|root = ./var/lib/containerd.|root = "/docker-graph/containerd/daemon"|' /etc/containerd/config.toml + sed -ie 's|state = ./run/containerd.|state = "/var/run/docker/containerd/daemon"|' /etc/containerd/config.toml sed -ie 's|enable_cdi = false|enable_cdi = true|' /etc/containerd/config.toml echo "starting containerd" @@ -1457,6 +1473,7 @@ if [[ "${START_MODE}" != *"nokubelet"* ]]; then Linux) install_cni_if_needed start_kubelet + wait_node_csr ;; *) print_color "Unsupported host OS. Must be Linux or Mac OS X, kubelet aborted." diff --git a/hack/logcheck.conf b/hack/logcheck.conf index 3fc412754c0c1..e8e3a11c4bae2 100644 --- a/hack/logcheck.conf +++ b/hack/logcheck.conf @@ -19,6 +19,7 @@ structured k8s.io/kubernetes/pkg/proxy/.* structured k8s.io/kms/.* structured k8s.io/apiserver/pkg/storage/value/.* structured k8s.io/apiserver/pkg/server/options/encryptionconfig/.* +structured k8s.io/kubernetes/pkg/credentialprovider/plugin/.* # The following packages have been migrated to contextual logging. # Packages matched here do not have to be listed above because @@ -46,17 +47,35 @@ contextual k8s.io/sample-controller/.* contextual k8s.io/kubernetes/cmd/kube-proxy/.* contextual k8s.io/kubernetes/cmd/kube-scheduler/.* contextual k8s.io/kubernetes/cmd/kubelet/.* +contextual k8s.io/kubernetes/pkg/api/.* +contextual k8s.io/kubernetes/pkg/apis/.* +contextual k8s.io/kubernetes/pkg/capabilities/.* +contextual k8s.io/kubernetes/pkg/client/.* +contextual k8s.io/kubernetes/pkg/cluster/.* contextual k8s.io/kubernetes/pkg/controller/.* +contextual k8s.io/kubernetes/pkg/features/.* +contextual k8s.io/kubernetes/pkg/fieldpath/.* +contextual k8s.io/kubernetes/pkg/generated/.* +contextual k8s.io/kubernetes/pkg/printers/.* +contextual k8s.io/kubernetes/pkg/quota/.* contextual k8s.io/kubernetes/pkg/scheduler/.* +contextual k8s.io/kubernetes/pkg/security/.* +contextual k8s.io/kubernetes/pkg/securitycontext/.* contextual k8s.io/kubernetes/test/e2e/dra/.* +contextual k8s.io/kubernetes/pkg/kubelet/certificate/.* +contextual k8s.io/kubernetes/pkg/kubelet/cm/devicemanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/dra/.* +contextual k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/.* contextual k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/.* +contextual k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/.* contextual k8s.io/kubernetes/pkg/kubelet/lifecycle/.* contextual k8s.io/kubernetes/pkg/kubelet/pleg/.* contextual k8s.io/kubernetes/pkg/kubelet/clustertrustbundle/.* contextual k8s.io/kubernetes/pkg/kubelet/token/.* contextual k8s.io/kubernetes/pkg/kubelet/cadvisor/.* +contextual k8s.io/kubernetes/pkg/kubelet/config/.* contextual k8s.io/kubernetes/pkg/kubelet/oom/.* +contextual k8s.io/kubernetes/pkg/kubelet/server/.* contextual k8s.io/kubernetes/pkg/kubelet/status/.* contextual k8s.io/kubernetes/pkg/kubelet/sysctl/.* contextual k8s.io/kubernetes/pkg/kubelet/winstats/.* @@ -67,6 +86,9 @@ contextual k8s.io/kubernetes/pkg/kubelet/nodeshutdown/.* contextual k8s.io/kubernetes/pkg/kubelet/pod/.* contextual k8s.io/kubernetes/pkg/kubelet/preemption/.* contextual k8s.io/kubernetes/pkg/kubelet/volumemanager/.* +contextual k8s.io/kubernetes/pkg/kubelet/util/.* +contextual k8s.io/kubernetes/pkg/kubelet/logs/.* +contextual k8s.io/kubernetes/test/images/sample-device-plugin/.* # As long as contextual logging is alpha or beta, all WithName, WithValues, # NewContext calls have to go through klog. Once it is GA, we can lift diff --git a/hack/make-rules/test-e2e-node.sh b/hack/make-rules/test-e2e-node.sh index 0ef5480c4daab..b42fd6d71318d 100755 --- a/hack/make-rules/test-e2e-node.sh +++ b/hack/make-rules/test-e2e-node.sh @@ -18,7 +18,6 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -kube::golang::setup_gomaxprocs # start the cache mutation detector by default so that cache mutators will be found KUBE_CACHE_MUTATION_DETECTOR="${KUBE_CACHE_MUTATION_DETECTOR:-true}" diff --git a/hack/make-rules/test-integration.sh b/hack/make-rules/test-integration.sh index ae208fa359115..716b3bf21af59 100755 --- a/hack/make-rules/test-integration.sh +++ b/hack/make-rules/test-integration.sh @@ -22,15 +22,17 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -kube::golang::setup_gomaxprocs kube::util::require-jq +set -x + # start the cache mutation detector by default so that cache mutators will be found KUBE_CACHE_MUTATION_DETECTOR="${KUBE_CACHE_MUTATION_DETECTOR:-true}" export KUBE_CACHE_MUTATION_DETECTOR -# panic the server on watch decode errors since they are considered coder mistakes -KUBE_PANIC_WATCH_DECODE_ERROR="${KUBE_PANIC_WATCH_DECODE_ERROR:-true}" +# default set to "false" to avoid panic on decode errors. +# integration tests intentionally insert data that cannot be decoded. +KUBE_PANIC_WATCH_DECODE_ERROR="${KUBE_PANIC_WATCH_DECODE_ERROR:-false}" export KUBE_PANIC_WATCH_DECODE_ERROR KUBE_INTEGRATION_TEST_MAX_CONCURRENCY=${KUBE_INTEGRATION_TEST_MAX_CONCURRENCY:-"-1"} @@ -47,6 +49,8 @@ KUBE_TEST_ARGS=${KUBE_TEST_ARGS:-} # Default glog module settings. KUBE_TEST_VMODULE=${KUBE_TEST_VMODULE:-""} +set +x + kube::test::find_integration_test_pkgs() { ( cd "${KUBE_ROOT}" diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index 8536a18ab4c7f..db8d6df6c0ccb 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -22,7 +22,6 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -kube::golang::setup_gomaxprocs kube::util::require-jq # start the cache mutation detector by default so that cache mutators will be found @@ -58,6 +57,8 @@ kube::test::find_go_packages() { ) } +set -x + # TODO: This timeout should really be lower, this is a *long* time to test one # package, however pkg/api/testing in particular will fail with a lower timeout # currently. We should attempt to lower this over time. @@ -86,6 +87,8 @@ KUBE_KEEP_VERBOSE_TEST_OUTPUT=${KUBE_KEEP_VERBOSE_TEST_OUTPUT:-n} # Set to 'false' to disable reduction of the JUnit file to only the top level tests. KUBE_PRUNE_JUNIT_TESTS=${KUBE_PRUNE_JUNIT_TESTS:-true} +set +x + kube::test::usage() { kube::log::usage_from_stdin </dev/null; then @@ -166,13 +163,6 @@ function codegen::protobuf() { # register: generate deep-copy functions and register them with a # scheme function codegen::deepcopy() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for deepcopy codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/code-generator/cmd/deepcopy-gen @@ -198,7 +188,7 @@ function codegen::deepcopy() { tag_pkgs+=("./$dir") done - kube::log::status "deepcopy: ${#tag_pkgs[@]} targets" + kube::log::status "Generating deepcopy code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running deepcopy-gen for:" for dir in "${tag_dirs[@]}"; do @@ -274,13 +264,6 @@ EOF # Some of the later codegens depend on the results of this, so it needs to come # first in the case of regenerating everything. function codegen::swagger() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for swagger codegen" - fi - return - fi - # Build the tool GOPROXY=off go install \ ./cmd/genswaggertypedocs @@ -288,7 +271,7 @@ function codegen::swagger() { local group_versions=() IFS=" " read -r -a group_versions <<< "meta/v1 meta/v1beta1 ${KUBE_AVAILABLE_GROUP_VERSIONS}" - kube::log::status "swagger: ${#group_versions[@]} targets" + kube::log::status "Generating swagger for ${#group_versions[@]} targets" git_find -z ':(glob)**/types_swagger_doc_generated.go' | xargs -0 rm -f @@ -304,13 +287,6 @@ function codegen::swagger() { # comment-tag in column 0 of one file of the form: # // +k8s:prerelease-lifecycle-gen=true function codegen::prerelease() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for prerelease codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/code-generator/cmd/prerelease-lifecycle-gen @@ -336,7 +312,7 @@ function codegen::prerelease() { tag_pkgs+=("./$dir") done - kube::log::status "prerelease-lifecycle: ${#tag_pkgs[@]} targets" + kube::log::status "Generating prerelease-lifecycle code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running prerelease-lifecycle-gen for:" for dir in "${tag_dirs[@]}"; do @@ -375,13 +351,6 @@ function codegen::prerelease() { # FIELDNAME: any object with a field of this name is a candidate # for having a defaulter generated function codegen::defaults() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for defaults codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/code-generator/cmd/defaulter-gen @@ -407,7 +376,7 @@ function codegen::defaults() { tag_pkgs+=("./$dir") done - kube::log::status "defaults: ${#tag_pkgs[@]} targets" + kube::log::status "Generating defaulter code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running defaulter-gen for:" for dir in "${tag_dirs[@]}"; do @@ -483,7 +452,7 @@ function codegen::validation() { time ) - kube::log::status "validation: ${#tag_pkgs[@]} targets" + kube::log::status "Generating validation code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running validation-gen for:" for dir in "${tag_dirs[@]}"; do @@ -491,19 +460,13 @@ function codegen::validation() { done fi - local lint_flag=() # empty arrays expand to no-value (as opposed to "") - if [[ -n "${LINT:-}" ]]; then - lint_flag+=("--lint") - else - git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f - fi + git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f validation-gen \ -v "${KUBE_VERBOSE}" \ --go-header-file "${BOILERPLATE_FILENAME}" \ --output-file "${output_file}" \ $(printf -- " --readonly-pkg %s" "${readonly_pkgs[@]}") \ - "${lint_flag[@]}" `# may expand to nothing` \ "${tag_pkgs[@]}" \ "$@" @@ -534,13 +497,6 @@ function codegen::validation() { # TODO: it might be better in the long term to make peer-types explicit in the # IDL. function codegen::conversions() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for conversions codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/code-generator/cmd/conversion-gen @@ -572,7 +528,7 @@ function codegen::conversions() { k8s.io/api/core/v1 ) - kube::log::status "conversion: ${#tag_pkgs[@]} targets" + kube::log::status "Generating conversion code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running conversion-gen for:" for dir in "${tag_dirs[@]}"; do @@ -602,13 +558,6 @@ function codegen::conversions() { # // +k8s:register-gen=package # function codegen::register() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for register codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/code-generator/cmd/register-gen @@ -634,7 +583,7 @@ function codegen::register() { tag_pkgs+=("./$dir") done - kube::log::status "register: ${#tag_pkgs[@]} targets" + kube::log::status "Generating register code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running register-gen for:" for dir in "${tag_dirs[@]}"; do @@ -680,13 +629,6 @@ function k8s_tag_files_except() { # comment-tag in column 0 of one file of the form: # // +k8s:openapi-gen=true function codegen::openapi() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for openapi codegen" - fi - return - fi - # Build the tool. GOPROXY=off go install \ k8s.io/kube-openapi/cmd/openapi-gen @@ -694,6 +636,8 @@ function codegen::openapi() { # The result file, in each pkg, of open-api generation. local output_file="${GENERATED_FILE_PREFIX}openapi.go" + local output_model_name_file="${GENERATED_FILE_PREFIX}model_name.go" + local output_dir="pkg/generated/openapi" local output_pkg="k8s.io/kubernetes/${output_dir}" local known_violations_file="${API_KNOWN_VIOLATIONS_DIR}/violation_exceptions.list" @@ -718,7 +662,7 @@ function codegen::openapi() { local tag_dirs=() kube::util::read-array tag_dirs < <( - grep -l --null '+k8s:openapi-gen=' "${tag_files[@]}" \ + grep -l --null '+k8s:openapi' "${tag_files[@]}" \ | while read -r -d $'\0' F; do dirname "${F}"; done \ | sort -u) @@ -731,7 +675,7 @@ function codegen::openapi() { tag_pkgs+=("./$dir") done - kube::log::status "openapi: ${#tag_pkgs[@]} targets" + kube::log::status "Generating openapi code" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running openapi-gen for:" for dir in "${tag_dirs[@]}"; do @@ -740,6 +684,7 @@ function codegen::openapi() { fi git_find -z ':(glob)pkg/generated/**'/"${output_file}" | xargs -0 rm -f + git_find -z ':(glob)pkg/generated/**'/"${output_model_name_file}" | xargs -0 rm -f openapi-gen \ -v "${KUBE_VERBOSE}" \ @@ -748,6 +693,7 @@ function codegen::openapi() { --output-dir "${output_dir}" \ --output-pkg "${output_pkg}" \ --report-filename "${report_file}" \ + --output-model-name-file "${output_model_name_file}" \ "${tag_pkgs[@]}" \ "$@" @@ -765,13 +711,6 @@ function codegen::openapi() { } function codegen::applyconfigs() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for applyconfigs codegen" - fi - return - fi - GOPROXY=off go install \ k8s.io/kubernetes/pkg/generated/openapi/cmd/models-schema \ k8s.io/code-generator/cmd/applyconfiguration-gen @@ -784,7 +723,7 @@ function codegen::applyconfigs() { | sort -u) ext_apis+=("k8s.io/apimachinery/pkg/apis/meta/v1") - kube::log::status "apply-config: ${#ext_apis[@]} targets" + kube::log::status "Generating apply-config code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running applyconfiguration-gen for:" for api in "${ext_apis[@]}"; do @@ -814,13 +753,6 @@ function codegen::applyconfigs() { } function codegen::clients() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for clients codegen" - fi - return - fi - GOPROXY=off go install \ k8s.io/code-generator/cmd/client-gen @@ -842,7 +774,7 @@ function codegen::clients() { gv_dirs+=("${pkg_dir}") done - kube::log::status "clients: ${#gv_dirs[@]} targets" + kube::log::status "Generating client code for ${#gv_dirs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running client-gen for:" for dir in "${gv_dirs[@]}"; do @@ -876,13 +808,6 @@ function codegen::clients() { } function codegen::listers() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for listers codegen" - fi - return - fi - GOPROXY=off go install \ k8s.io/code-generator/cmd/lister-gen @@ -893,7 +818,7 @@ function codegen::listers() { | while read -r -d $'\0' F; do dirname "${F}"; done \ | sort -u) - kube::log::status "listers: ${#ext_apis[@]} targets" + kube::log::status "Generating lister code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running lister-gen for:" for api in "${ext_apis[@]}"; do @@ -923,13 +848,6 @@ function codegen::listers() { } function codegen::informers() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for informers codegen" - fi - return - fi - GOPROXY=off go install \ k8s.io/code-generator/cmd/informer-gen @@ -940,7 +858,7 @@ function codegen::informers() { | while read -r -d $'\0' F; do dirname "${F}"; done \ | sort -u) - kube::log::status "informers: code for ${#ext_apis[@]} targets" + kube::log::status "Generating informer code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running informer-gen for:" for api in "${ext_apis[@]}"; do @@ -979,13 +897,6 @@ function indent() { } function codegen::subprojects() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for subprojects codegen" - fi - return - fi - # Call generation on sub-projects. local subs=( staging/src/k8s.io/code-generator/examples @@ -1000,7 +911,7 @@ function codegen::subprojects() { local codegen codegen="${KUBE_ROOT}/staging/src/k8s.io/code-generator" for sub in "${subs[@]}"; do - kube::log::status "subproject ${sub}:" + kube::log::status "Generating code for subproject ${sub}" pushd "${sub}" >/dev/null CODEGEN_PKG="${codegen}" \ UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS}" \ @@ -1011,19 +922,10 @@ function codegen::subprojects() { } function codegen::protobindings() { - if [[ -n "${LINT:-}" ]]; then - if [[ "${KUBE_VERBOSE}" -gt 2 ]]; then - kube::log::status "No linter for protobindings codegen" - fi - return - fi - # Each element of this array is a directory containing subdirectories which # eventually contain a file named "api.proto". - local apis_using_gogo=( + local apis=( "staging/src/k8s.io/kubelet/pkg/apis/dra" - ) - local apis_using_protoc=( "staging/src/k8s.io/kubelet/pkg/apis/deviceplugin" "staging/src/k8s.io/kubelet/pkg/apis/podresources" "staging/src/k8s.io/kms/apis" @@ -1034,9 +936,8 @@ function codegen::protobindings() { "staging/src/k8s.io/externaljwt/apis" "staging/src/k8s.io/kubelet/pkg/apis/dra-health" ) - local apis=("${apis_using_gogo[@]}" "${apis_using_protoc[@]}") - kube::log::status "protobuf bindings: ${#apis[@]} targets" + kube::log::status "Generating protobuf bindings for ${#apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: generating protobuf bindings for:" for dir in "${apis[@]}"; do @@ -1050,15 +951,11 @@ function codegen::protobindings() { done if kube::protoc::check_protoc >/dev/null; then - hack/_update-generated-proto-bindings-dockerized.sh gogo "${apis_using_gogo[@]}" - hack/_update-generated-proto-bindings-dockerized.sh protoc "${apis_using_protoc[@]}" + hack/_update-generated-proto-bindings-dockerized.sh "${apis[@]}" else kube::log::status "protoc ${PROTOC_VERSION} not found (can install with hack/install-protoc.sh); generating containerized..." - # NOTE: All output from this script needs to be copied back to the calling - # source tree. This is managed in kube::build::copy_output in build/common.sh. - # If the output set is changed update that function. - build/run.sh hack/_update-generated-proto-bindings-dockerized.sh gogo "${apis_using_gogo[@]}" - build/run.sh hack/_update-generated-proto-bindings-dockerized.sh protoc "${apis_using_protoc[@]}" + + build/run.sh hack/_update-generated-proto-bindings-dockerized.sh "${apis[@]}" fi } diff --git a/hack/update-mocks.sh b/hack/update-mocks.sh index eb2b9760b8f1b..f84bb36852199 100755 --- a/hack/update-mocks.sh +++ b/hack/update-mocks.sh @@ -27,7 +27,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env echo 'installing mockery' -GOTOOLCHAIN="$(kube::golang::hack_tools_gotoolchain)" go -C "${KUBE_ROOT}/hack/tools" install github.com/vektra/mockery/v2 +GOTOOLCHAIN="$(kube::golang::hack_tools_gotoolchain)" go -C "${KUBE_ROOT}/hack/tools" install github.com/vektra/mockery/v3 function git_grep() { git grep --untracked --exclude-standard \ @@ -42,7 +42,7 @@ function git_grep() { cd "${KUBE_ROOT}" -GENERATED_MOCK_FILE_REGEX="^// Code generated by mockery v[0-9.]\+. DO NOT EDIT.$" +GENERATED_MOCK_FILE_REGEX="^// Code generated by mockery.* DO NOT EDIT.$" git_grep -l -z "${GENERATED_MOCK_FILE_REGEX}" | xargs -0 rm -f diff --git a/hack/verify-api-lint.sh b/hack/verify-api-lint.sh deleted file mode 100755 index 10764686923af..0000000000000 --- a/hack/verify-api-lint.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -# 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. - -# This script runs API lint tools. -# -# Usage: `hack/verify-api-lint.sh`. - -set -o errexit -set -o nounset -set -o pipefail - -KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source "${KUBE_ROOT}/hack/lib/init.sh" - -cd "${KUBE_ROOT}" - -LINT=true hack/update-codegen.sh diff --git a/hack/verify-e2e-images.sh b/hack/verify-e2e-images.sh index faa28880e9b38..7c99a73d727b6 100755 --- a/hack/verify-e2e-images.sh +++ b/hack/verify-e2e-images.sh @@ -22,6 +22,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. cd "${KUBE_ROOT}" source hack/lib/init.sh +ret=0 # NOTE: Please do NOT add any to this list!! # @@ -34,16 +35,37 @@ kube::util::read-array PERMITTED_IMAGES < <(sed '/^#/d' ./test/images/.permitted echo "Getting e2e image list ..." make WHAT=test/e2e/e2e.test e2e_test="$(kube::util::find-binary e2e.test)" -kube::util::read-array IMAGES < <("${e2e_test}" --list-images | sed -E 's/^(.+):[^:]+$/\1/' | LC_ALL=C sort -u) + +# validate "e2e.test --list-images": +# - no unexpected output (whether it's on stderr or stdout) +# - zero exit code (indirectly ensures that tests are set up properly) +output=$("${e2e_test}" --list-images 2>&1) || ret=$? +if [[ $ret -ne 0 ]]; then + >&2 echo "FAIL: '${e2e_test} --list-images' failed:" + >&2 echo "${output}" + exit 1 +fi + +unexpected_output=$(echo "${output}" | grep -v -E '^([[:alnum:]/.-]+):[^:]+$' || true) +if [[ -n "${unexpected_output}" ]]; then + >&2 echo "FAIL: '${e2e_test} --list-images' printed unexpected output:" + >&2 echo "${unexpected_output}" + exit 1 +fi + +# extract image names without the version +kube::util::read-array IMAGES < <(echo "${output}" | sed -E 's/^([[:alnum:]/.-]+):[^:]+$/\1/' | LC_ALL=C sort -u) # diff versus known permitted images -ret=0 >&2 echo "Diffing e2e image list ..." -diff -Naupr <(printf '%s\n' "${IMAGES[@]}") <(printf '%s\n' "${PERMITTED_IMAGES[@]}") || ret=$? +# diff context is irrelevant here because of sorting. +# Instead we want to know about old images (no longer in use, need to be removed) +# and new images (should not get added). +diff <(printf '%s\n' "${PERMITTED_IMAGES[@]}") <(printf '%s\n' "${IMAGES[@]}") | sed -E -e '/^---$/d' -e '/^[[:digit:]]+[acd][[:digit:]]+$/d' -e 's/^/forbidden image:/' >&2 || ret=$? if [[ $ret -eq 0 ]]; then >&2 echo "PASS: e2e images used are OK." else - >&2 echo "FAIL: e2e images do not match the approved list!" + >&2 echo "FAIL: current e2e images do not match the approved list in test/images/.permitted-images!" >&2 echo "" >&2 echo "Please use registry.k8s.io/e2e-test-images/agnhost wherever possible, we are consolidating test images." >&2 echo "See: test/images/agnhost/README.md" diff --git a/hack/verify-golangci-lint-pr.sh b/hack/verify-golangci-lint-pr.sh deleted file mode 100755 index 689028add309d..0000000000000 --- a/hack/verify-golangci-lint-pr.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2022 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. - -# This script checks a PR for the coding style for the Go language files using -# golangci-lint. It does nothing when invoked as part of a normal "make -# verify". - -set -o nounset -set -o pipefail - -if [ ! "${PULL_NUMBER:-}" ]; then - echo 'Not testing anything because this is not a pull request.' - exit 0 -fi - -KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. - -"${KUBE_ROOT}/hack/verify-golangci-lint.sh" -r "${PULL_BASE_SHA}" -s diff --git a/hack/verify-golangci-lint.sh b/hack/verify-golangci-lint.sh index 91550203cbe35..b2e00b9e8e572 100755 --- a/hack/verify-golangci-lint.sh +++ b/hack/verify-golangci-lint.sh @@ -21,7 +21,7 @@ usage () { cat <&2 -Usage: $0 [-r |-a] [-s] [-c none|] [-- ] [packages]" +Usage: $0 [-r |-a] [-c none|] [-- ] [packages]" -r : only report issues in code added since that revision -a: automatically select the common base of origin/master and HEAD as revision @@ -54,7 +54,7 @@ golangci_config="${KUBE_ROOT}/hack/golangci.yaml" base= hints= githubactions= -while getopts "ar:sng:c:" o; do +while getopts "ar:ng:c:" o; do case "${o}" in a) base="$(git merge-base origin/master HEAD)" diff --git a/hack/verify-test-featuregates.sh b/hack/verify-test-featuregates.sh index 80f7af87ac2d3..947fa250f6cdd 100755 --- a/hack/verify-test-featuregates.sh +++ b/hack/verify-test-featuregates.sh @@ -41,19 +41,4 @@ if [[ -n "${direct_sets}" ]]; then rc=1 fi -export LC_ALL=C -# ensure all generic features are added in alphabetic order -lines=$(git grep 'genericfeatures[.].*:' -- pkg/features/kube_features.go) -sorted_lines=$(echo "$lines" | sort) -if [[ "$lines" != "$sorted_lines" ]]; then - echo "Generic features in pkg/features/kube_features.go not sorted" >&2 - echo >&2 - echo "Expected:" >&2 - echo "$sorted_lines" >&2 - echo >&2 - echo "Got:" >&2 - echo "$lines" >&2 - rc=1 -fi - exit $rc diff --git a/openshift-hack/cmd/k8s-tests-ext/disabled_tests.go b/openshift-hack/cmd/k8s-tests-ext/disabled_tests.go index ff61abb8a2108..f3f6efc2d58ea 100644 --- a/openshift-hack/cmd/k8s-tests-ext/disabled_tests.go +++ b/openshift-hack/cmd/k8s-tests-ext/disabled_tests.go @@ -161,11 +161,23 @@ func filterOutDisabledSpecs(specs et.ExtensionTestSpecs) et.ExtensionTestSpecs { // https://issues.redhat.com/browse/OCPBUGS-45275 "[sig-network] Connectivity Pod Lifecycle should be able to connect to other Pod from a terminating Pod", + + // https://issues.redhat.com/browse/OCPBUGS-73813 + "[sig-storage] MutableCSINodeAllocatableCount [FeatureGate:MutableCSINodeAllocatableCount] [Beta]", + + // https://issues.redhat.com/browse/OCPBUGS-63132 + "[sig-node] [Serial] Pod InPlace Resize Container (deferred-resizes) [FeatureGate:InPlacePodVerticalScaling] pod-resize-retry-deferred-test-3", }, // tests that need to be temporarily disabled while the rebase is in progress. "RebaseInProgress": { + // https://issues.redhat.com/browse/OCPBUGS-61515 + "[sig-scheduling] SchedulerPreemption [Serial] validates various priority Pods preempt expectedly with the async preemption [Feature:SchedulerAsyncPreemption] [FeatureGate:SchedulerAsyncPreemption] [Beta]", + // https://issues.redhat.com/browse/OCPBUGS-61378 "[sig-network] Conntrack should be able to cleanup conntrack entries when UDP service target port changes for a NodePort service", + + // https://issues.redhat.com/browse/OCPBUGS-77243 + "[sig-node] Container Runtime blackbox test when running a container with a new image [Serial] should be able to pull from private registry with secret [NodeConformance]", }, // tests that may work, but we don't support them "Unsupported": { diff --git a/openshift-hack/cmd/k8s-tests-ext/k8s-tests.go b/openshift-hack/cmd/k8s-tests-ext/k8s-tests.go index 191956c54a11f..640062df5fc45 100644 --- a/openshift-hack/cmd/k8s-tests-ext/k8s-tests.go +++ b/openshift-hack/cmd/k8s-tests-ext/k8s-tests.go @@ -14,6 +14,7 @@ import ( "github.com/openshift-eng/openshift-tests-extension/pkg/cmd" e "github.com/openshift-eng/openshift-tests-extension/pkg/extension" + ext "github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests" g "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo" v "github.com/openshift-eng/openshift-tests-extension/pkg/version" @@ -88,8 +89,8 @@ func main() { hpaTestTimeout := time.Minute * 30 kubeTestsExtension.AddSuite(e.Suite{ Name: "kubernetes/autoscaling/hpa", - Qualifiers: []string{"name.contains('[Feature:HPA]')"}, // Note that this does not use withExcludedTestsFilter to be able to run DedicatedJob labelled tests. - Parallelism: 3, // HPA tests have high CPU + memory usage, so we cannot have a high level of parallelism here. + Qualifiers: []string{"name.contains('[Feature:HPA]') || name.contains('[Feature:HPAConfigurableTolerance]')"}, // Note that this does not use withExcludedTestsFilter to be able to run DedicatedJob labelled tests. + Parallelism: 3, // HPA tests have high CPU + memory usage, so we cannot have a high level of parallelism here. TestTimeout: &hpaTestTimeout, }) @@ -99,10 +100,10 @@ func main() { kubeTestsExtension.RegisterImage(image) } - //FIXME(stbenjam): what other suites does k8s-test contribute to? + // FIXME(stbenjam): what other suites does k8s-test contribute to? // Build our specs from ginkgo - specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite() + specs, err := g.BuildExtensionTestSpecsFromOpenShiftGinkgoSuite(ext.AllTestsIncludingVendored()) if err != nil { panic(err) } diff --git a/openshift-hack/cmd/k8s-tests-ext/labels.go b/openshift-hack/cmd/k8s-tests-ext/labels.go index f26cf2811edb3..9a03860704f50 100644 --- a/openshift-hack/cmd/k8s-tests-ext/labels.go +++ b/openshift-hack/cmd/k8s-tests-ext/labels.go @@ -49,6 +49,7 @@ func addLabelsToSpecs(specs et.ExtensionTestSpecs) { // tests that will be configured to run manually through their own dedicated prow jobs "[DedicatedJob]": { "[Feature:HPA]", + "[Feature:HPAConfigurableTolerance]", }, } diff --git a/openshift-hack/images/hyperkube/Dockerfile.rhel b/openshift-hack/images/hyperkube/Dockerfile.rhel index edb5597e82617..34752d13f7e6f 100644 --- a/openshift-hack/images/hyperkube/Dockerfile.rhel +++ b/openshift-hack/images/hyperkube/Dockerfile.rhel @@ -1,4 +1,4 @@ -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS builder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -9,10 +9,10 @@ RUN make TAGS="${TAGS}" WHAT='cmd/kube-apiserver cmd/kube-controller-manager cmd /tmp/build && \ gzip /tmp/build/k8s-tests-ext -FROM registry.ci.openshift.org/ocp/4.21:base-rhel9 +FROM registry.ci.openshift.org/ocp/4.22:base-rhel9 RUN yum install -y --setopt=tsflags=nodocs --setopt=skip_missing_names_on_install=False iproute && yum clean all 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.35.0" \ No newline at end of file diff --git a/openshift-hack/images/installer-kube-apiserver-artifacts/Dockerfile.rhel b/openshift-hack/images/installer-kube-apiserver-artifacts/Dockerfile.rhel index 3c63939f5ca57..985ed96da373e 100644 --- a/openshift-hack/images/installer-kube-apiserver-artifacts/Dockerfile.rhel +++ b/openshift-hack/images/installer-kube-apiserver-artifacts/Dockerfile.rhel @@ -2,7 +2,7 @@ # the kube-apiserver layered on top of the cluster-native Linux installer image. # The resulting image is used to build the openshift-install binary. -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS macbuilder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS macbuilder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -10,7 +10,7 @@ ENV KUBE_BUILD_PLATFORMS=darwin/amd64 ENV KUBE_STATIC_OVERRIDES=kube-apiserver RUN make WHAT='cmd/kube-apiserver' -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS macarmbuilder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS macarmbuilder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -18,7 +18,7 @@ ENV KUBE_BUILD_PLATFORMS=darwin/arm64 ENV KUBE_STATIC_OVERRIDES=kube-apiserver RUN make WHAT='cmd/kube-apiserver' -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS linuxbuilder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS linuxbuilder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -27,7 +27,7 @@ ENV KUBE_BUILD_PLATFORMS=linux/amd64 ENV KUBE_STATIC_OVERRIDES=kube-apiserver RUN make WHAT='cmd/kube-apiserver' -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS linuxarmbuilder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS linuxarmbuilder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -36,7 +36,7 @@ ENV KUBE_BUILD_PLATFORMS=linux/arm64 ENV KUBE_STATIC_OVERRIDES=kube-apiserver RUN make WHAT='cmd/kube-apiserver' -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS builder ARG TAGS="" WORKDIR /go/src/k8s.io/kubernetes COPY . . @@ -44,7 +44,7 @@ ENV GO_COMPLIANCE_EXCLUDE=".*" ENV KUBE_STATIC_OVERRIDES=kube-apiserver RUN make WHAT='cmd/kube-apiserver' -FROM registry.ci.openshift.org/ocp/4.21:base-rhel9 +FROM registry.ci.openshift.org/ocp/4.22:base-rhel9 COPY --from=macbuilder /go/src/k8s.io/kubernetes/_output/local/bin/darwin/amd64/kube-apiserver /usr/share/openshift/darwin/amd64/kube-apiserver COPY --from=macarmbuilder /go/src/k8s.io/kubernetes/_output/local/bin/darwin/arm64/kube-apiserver /usr/share/openshift/darwin/arm64/kube-apiserver COPY --from=linuxbuilder /go/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/kube-apiserver /usr/share/openshift/linux/amd64/kube-apiserver diff --git a/openshift-hack/images/kube-proxy/Dockerfile.rhel b/openshift-hack/images/kube-proxy/Dockerfile.rhel index e1e7f5d18f777..29af8385c08a2 100644 --- a/openshift-hack/images/kube-proxy/Dockerfile.rhel +++ b/openshift-hack/images/kube-proxy/Dockerfile.rhel @@ -1,11 +1,11 @@ -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS builder WORKDIR /go/src/k8s.io/kubernetes COPY . . RUN make WHAT='cmd/kube-proxy' && \ mkdir -p /tmp/build && \ cp /go/src/k8s.io/kubernetes/_output/local/bin/linux/$(go env GOARCH)/kube-proxy /tmp/build -FROM registry.ci.openshift.org/ocp/4.21:base-rhel9 +FROM registry.ci.openshift.org/ocp/4.22:base-rhel9 RUN INSTALL_PKGS="conntrack-tools iptables nftables" && \ yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \ yum clean all && rm -rf /var/cache/* diff --git a/openshift-hack/images/tests/Dockerfile.rhel b/openshift-hack/images/tests/Dockerfile.rhel index 10a2b280a2234..095050c337133 100644 --- a/openshift-hack/images/tests/Dockerfile.rhel +++ b/openshift-hack/images/tests/Dockerfile.rhel @@ -1,4 +1,4 @@ -FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder +FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.25-openshift-4.22 AS builder WORKDIR /go/src/k8s.io/kubernetes COPY . . RUN make WHAT=openshift-hack/e2e/k8s-e2e.test; \ @@ -11,7 +11,7 @@ RUN make WHAT=openshift-hack/e2e/k8s-e2e.test; \ cp /go/src/k8s.io/kubernetes/openshift-hack/test-kubernetes-e2e.sh /tmp/build/; \ cp /go/src/k8s.io/kubernetes/openshift-hack/images/kube-proxy/test-kube-proxy.sh /tmp/build/ -FROM registry.ci.openshift.org/ocp/4.21:tools +FROM registry.ci.openshift.org/ocp/4.22:tools COPY --from=builder /tmp/build/k8s-e2e.test /usr/bin/ COPY --from=builder /tmp/build/ginkgo /usr/bin/ COPY --from=builder /tmp/build/k8s-tests-ext /usr/bin/ diff --git a/openshift-kube-apiserver/admission/customresourcevalidation/dns/validate_dns.go b/openshift-kube-apiserver/admission/customresourcevalidation/dns/validate_dns.go index 0ae18e8f7e684..4a7e02a5fcd1b 100644 --- a/openshift-kube-apiserver/admission/customresourcevalidation/dns/validate_dns.go +++ b/openshift-kube-apiserver/admission/customresourcevalidation/dns/validate_dns.go @@ -143,7 +143,7 @@ func validateTolerations(versionedTolerations []corev1.Toleration, fldPath *fiel allErrors = append(allErrors, field.Invalid(fldPath.Index(i), unversionedTolerations[i], err.Error())) } } - allErrors = append(allErrors, apivalidation.ValidateTolerations(unversionedTolerations, fldPath)...) + allErrors = append(allErrors, apivalidation.ValidateTolerations(unversionedTolerations, fldPath, apivalidation.PodValidationOptions{})...) return allErrors } diff --git a/openshift-kube-apiserver/openshiftkubeapiserver/patch.go b/openshift-kube-apiserver/openshiftkubeapiserver/patch.go index ce029240f67a9..7fd68e6468244 100644 --- a/openshift-kube-apiserver/openshiftkubeapiserver/patch.go +++ b/openshift-kube-apiserver/openshiftkubeapiserver/patch.go @@ -73,7 +73,7 @@ func OpenShiftKubeAPIServerConfigPatch(genericConfig *genericapiserver.Config, k clusterresourcequota.NewInitializer( openshiftInformers.getOpenshiftQuotaInformers().Quota().V1().ClusterResourceQuotas(), clusterQuotaMappingController.GetClusterQuotaMapper(), - generic.NewRegistry(install.NewQuotaConfigurationForAdmission().Evaluators()), + generic.NewRegistry(install.NewQuotaConfigurationForAdmission(kubeInformers).Evaluators()), ), nodeenv.NewInitializer(enablement.OpenshiftConfig().ProjectConfig.DefaultNodeSelector), admissionrestconfig.NewInitializer(*rest.CopyConfig(genericConfig.LoopbackClientConfig)), diff --git a/openshift-kube-controller-manager/servicecacertpublisher/publisher.go b/openshift-kube-controller-manager/servicecacertpublisher/publisher.go index af17ee9802650..babef3fdc7cdb 100644 --- a/openshift-kube-controller-manager/servicecacertpublisher/publisher.go +++ b/openshift-kube-controller-manager/servicecacertpublisher/publisher.go @@ -73,22 +73,22 @@ type Publisher struct { } // Run starts process -func (c *Publisher) Run(workers int, stopCh <-chan struct{}) { +func (c *Publisher) Run(ctx context.Context, workers int) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() klog.Infof("Starting service CA certificate configmap publisher") defer klog.Infof("Shutting down service CA certificate configmap publisher") - if !cache.WaitForNamedCacheSync("crt configmap", stopCh, c.cmListerSynced) { + if !cache.WaitForNamedCacheSync("crt configmap", ctx.Done(), c.cmListerSynced) { return } for i := 0; i < workers; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) + go wait.Until(c.runWorker, time.Second, ctx.Done()) } - <-stopCh + <-ctx.Done() } func (c *Publisher) configMapDeleted(obj interface{}) { diff --git a/pkg/api/persistentvolumeclaim/util_test.go b/pkg/api/persistentvolumeclaim/util_test.go index 61b6cb348447a..f28f63f3f9aa4 100644 --- a/pkg/api/persistentvolumeclaim/util_test.go +++ b/pkg/api/persistentvolumeclaim/util_test.go @@ -272,8 +272,10 @@ func TestDataSourceFilter(t *testing.T) { t.Run(testName, func(t *testing.T) { // TODO: this will be removed in 1.36 featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.32")) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, test.xnsEnabled) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.AnyVolumeDataSource: test.anyEnabled, + features.CrossNamespaceVolumeDataSource: test.xnsEnabled, + }) DropDisabledFields(&test.spec, &test.oldSpec) if test.spec.DataSource != test.want { t.Errorf("expected condition was not met, test: %s, anyEnabled: %v, xnsEnabled: %v, spec: %+v, expected DataSource: %+v", @@ -370,8 +372,10 @@ func TestDataSourceRef(t *testing.T) { }, } - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, true) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, true) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.AnyVolumeDataSource: true, + features.CrossNamespaceVolumeDataSource: true, + }) for testName, test := range tests { t.Run(testName, func(t *testing.T) { @@ -598,8 +602,10 @@ func TestDropDisabledFieldsFromStatus(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.enableRecoverVolumeExpansionFailure) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeAttributesClass, test.enableVolumeAttributesClass) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.RecoverVolumeExpansionFailure: test.enableRecoverVolumeExpansionFailure, + features.VolumeAttributesClass: test.enableVolumeAttributesClass, + }) DropDisabledFieldsFromStatus(test.pvc, test.oldPVC) diff --git a/pkg/api/pod/testing/make.go b/pkg/api/pod/testing/make.go index 552d795ff7861..e018a0a6371bf 100644 --- a/pkg/api/pod/testing/make.go +++ b/pkg/api/pod/testing/make.go @@ -250,6 +250,12 @@ func SetObjectMeta(objectMeta metav1.ObjectMeta) Tweak { } } +func SetWorkloadRef(workloadRef *api.WorkloadReference) Tweak { + return func(pod *api.Pod) { + pod.Spec.WorkloadRef = workloadRef + } +} + func MakeContainer(name string, tweaks ...TweakContainer) api.Container { cnr := api.Container{ Name: name, Image: "image", ImagePullPolicy: "IfNotPresent", diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index 464e31a56c4d3..1d0ae2209b756 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -424,10 +424,13 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po AllowSidecarResizePolicy: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling), AllowMatchLabelKeysInPodTopologySpread: utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpread), AllowMatchLabelKeysInPodTopologySpreadSelectorMerge: utilfeature.DefaultFeatureGate.Enabled(features.MatchLabelKeysInPodTopologySpreadSelectorMerge), + InPlacePodLevelResourcesVerticalScalingEnabled: utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling), OldPodViolatesMatchLabelKeysValidation: false, OldPodViolatesLegacyMatchLabelKeysValidation: false, AllowContainerRestartPolicyRules: utilfeature.DefaultFeatureGate.Enabled(features.ContainerRestartRules), AllowUserNamespacesWithVolumeDevices: false, + // This also allows restart rules on sidecar containers. + AllowRestartAllContainers: utilfeature.DefaultFeatureGate.Enabled(features.RestartAllContainersOnContainerExits), } // If old spec uses relaxed validation or enabled the RelaxedEnvironmentVariableValidation feature gate, @@ -435,8 +438,10 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po opts.AllowRelaxedEnvironmentVariableValidation = useRelaxedEnvironmentVariableValidation(podSpec, oldPodSpec) opts.AllowRelaxedDNSSearchValidation = useRelaxedDNSSearchValidation(oldPodSpec) opts.AllowEnvFilesValidation = useAllowEnvFilesValidation(oldPodSpec) + opts.AllowUserNamespacesHostNetworkSupport = useAllowUserNamespacesHostNetworkSupport(oldPodSpec) opts.AllowOnlyRecursiveSELinuxChangePolicy = useOnlyRecursiveSELinuxChangePolicy(oldPodSpec) + opts.AllowTaintTolerationComparisonOperators = allowTaintTolerationComparisonOperators(oldPodSpec) if oldPodSpec != nil { // if old spec used non-integer multiple of huge page unit size, we must allow it @@ -474,6 +479,7 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po opts.AllowSidecarResizePolicy = opts.AllowSidecarResizePolicy || hasRestartableInitContainerResizePolicy(oldPodSpec) opts.AllowContainerRestartPolicyRules = opts.AllowContainerRestartPolicyRules || containerRestartRulesInUse(oldPodSpec) + opts.AllowRestartAllContainers = opts.AllowRestartAllContainers || restartAllContainersActionInUse(oldPodSpec) // If old spec has userns and volume devices (doesn't work), we still allow // modifications to it. @@ -535,6 +541,23 @@ func hasDotOrUnderscore(searches []string) bool { return false } +func useAllowUserNamespacesHostNetworkSupport(oldPodSpec *api.PodSpec) bool { + // Return true early if feature gate is enabled + if utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesHostNetworkSupport) { + return true + } + + if oldPodSpec == nil || oldPodSpec.SecurityContext == nil || oldPodSpec.SecurityContext.HostUsers == nil { + return false + } + + // If a pod with user namespaces and hostNetwork already exists in the cluster, + // this allows it to continue using the UserNamespacesHostNetworkSupport + // validation logic even after the feature gate is disabled. + userNamespaces := !*oldPodSpec.SecurityContext.HostUsers + return oldPodSpec.SecurityContext.HostNetwork && userNamespaces +} + func useAllowEnvFilesValidation(oldPodSpec *api.PodSpec) bool { // Return true early if feature gate is enabled if utilfeature.DefaultFeatureGate.Enabled(features.EnvFiles) { @@ -731,6 +754,7 @@ func dropDisabledFields( dropDisabledDynamicResourceAllocationFields(podSpec, oldPodSpec) dropDisabledClusterTrustBundleProjection(podSpec, oldPodSpec) dropDisabledPodCertificateProjection(podSpec, oldPodSpec) + dropDisabledWorkloadRef(podSpec, oldPodSpec) if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) { // Drop ResizePolicy fields. Don't drop updates to Resources field as template.spec.resources @@ -983,6 +1007,12 @@ func dropDisabledPodStatusFields(podStatus, oldPodStatus *api.PodStatus, podSpec podStatus = &api.PodStatus{} } + if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodLevelResourcesVerticalScaling) && !podLevelStatusResourcesInUse(oldPodStatus) { + // Drop Resources and AllocatedResources fields from PodStatus + podStatus.Resources = nil + podStatus.AllocatedResources = nil + } + if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) && !inPlacePodVerticalScalingInUse(oldPodSpec) { // Drop Resources fields dropResourcesField := func(csl []api.ContainerStatus) { @@ -1303,6 +1333,16 @@ func podLevelResourcesInUse(podSpec *api.PodSpec) bool { return false } +// podLevelStatusResourcesInUse checks if AllocationResources or Resources are set +// in PodStatus. +func podLevelStatusResourcesInUse(podStatus *api.PodStatus) bool { + if podStatus == nil { + return false + } + + return podStatus.Resources != nil || podStatus.AllocatedResources != nil +} + // inPlacePodVerticalScalingInUse returns true if pod spec is non-nil and ResizePolicy is set func inPlacePodVerticalScalingInUse(podSpec *api.PodSpec) bool { if podSpec == nil { @@ -1599,6 +1639,28 @@ func useOnlyRecursiveSELinuxChangePolicy(oldPodSpec *api.PodSpec) bool { return true } +func taintTolerationComparisonOperatorsInUse(podSpec *api.PodSpec) bool { + if podSpec == nil { + return false + } + for _, toleration := range podSpec.Tolerations { + if toleration.Operator == api.TolerationOpLt || toleration.Operator == api.TolerationOpGt { + return true + } + } + return false +} + +func allowTaintTolerationComparisonOperators(oldPodSpec *api.PodSpec) bool { + // allow the operators if the feature gate is enabled or the old pod spec uses + // comparison operators + if utilfeature.DefaultFeatureGate.Enabled(features.TaintTolerationComparisonOperators) || + taintTolerationComparisonOperatorsInUse(oldPodSpec) { + return true + } + return false +} + func hasUserNamespacesWithVolumeDevices(podSpec *api.PodSpec) bool { if podSpec.SecurityContext == nil || podSpec.SecurityContext.HostUsers == nil || *podSpec.SecurityContext.HostUsers { return false @@ -1768,3 +1830,44 @@ func containerRestartRulesInUse(oldPodSpec *api.PodSpec) bool { } return false } + +// dropDisabledWorkloadRef removes pod workload reference from its spec +// unless it is already used by the old pod spec. +func dropDisabledWorkloadRef(podSpec, oldPodSpec *api.PodSpec) { + if !utilfeature.DefaultFeatureGate.Enabled(features.GenericWorkload) && !workloadRefInUse(oldPodSpec) { + podSpec.WorkloadRef = nil + } +} + +func workloadRefInUse(podSpec *api.PodSpec) bool { + if podSpec == nil { + return false + } + + return podSpec.WorkloadRef != nil +} + +func restartAllContainersActionInUse(oldPodSpec *api.PodSpec) bool { + if oldPodSpec == nil { + return false + } + for _, c := range oldPodSpec.Containers { + for _, rule := range c.RestartPolicyRules { + if rule.Action == api.ContainerRestartRuleActionRestartAllContainers { + return true + } + } + } + for _, c := range oldPodSpec.InitContainers { + for _, rule := range c.RestartPolicyRules { + if rule.Action == api.ContainerRestartRuleActionRestartAllContainers { + return true + } + } + // This feature also allows sidecar containers to have rules. + if c.RestartPolicy != nil && *c.RestartPolicy == api.ContainerRestartPolicyAlways && len(c.RestartPolicyRules) > 0 { + return true + } + } + return false +} diff --git a/pkg/api/pod/util_test.go b/pkg/api/pod/util_test.go index 1ad61379fcdfc..3701535c04952 100644 --- a/pkg/api/pod/util_test.go +++ b/pkg/api/pod/util_test.go @@ -1058,8 +1058,13 @@ func TestDropDynamicResourceAllocation(t *testing.T) { for _, tc := range testcases { t.Run(tc.description, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicResourceAllocation, tc.enabled) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAExtendedResource, tc.extendedEnabled) + if !tc.enabled { + featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34")) + } + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.DynamicResourceAllocation: tc.enabled, + features.DRAExtendedResource: tc.extendedEnabled, + }) oldPod := tc.oldPod.DeepCopy() newPod := tc.newPod.DeepCopy() @@ -1229,97 +1234,47 @@ func TestDropDisabledPodStatusFields_ObservedGeneration(t *testing.T) { name string podStatus *api.PodStatus oldPodStatus *api.PodStatus - featureGateOn bool wantPodStatus *api.PodStatus }{ - { - name: "old=without, new=without / feature gate off", - oldPodStatus: podWithoutObservedGen(), - podStatus: podWithoutObservedGen(), - featureGateOn: false, - wantPodStatus: podWithoutObservedGen(), - }, { name: "old=without, new=without / feature gate on", oldPodStatus: podWithoutObservedGen(), podStatus: podWithoutObservedGen(), - featureGateOn: true, - wantPodStatus: podWithoutObservedGen(), - }, - { - name: "old=without, new=with / feature gate off", - oldPodStatus: podWithoutObservedGen(), - podStatus: podWithObservedGen(), - featureGateOn: false, wantPodStatus: podWithoutObservedGen(), }, { name: "old=with, new=without / feature gate on", oldPodStatus: podWithObservedGen(), podStatus: podWithoutObservedGen(), - featureGateOn: true, wantPodStatus: podWithoutObservedGen(), }, - { - name: "old=with, new=with / feature gate off", - oldPodStatus: podWithObservedGen(), - podStatus: podWithObservedGen(), - featureGateOn: false, - wantPodStatus: podWithObservedGen(), - }, { name: "old=with, new=with / feature gate on", oldPodStatus: podWithObservedGen(), podStatus: podWithObservedGen(), - featureGateOn: true, wantPodStatus: podWithObservedGen(), }, - { - name: "old=without, new=withInConditions / feature gate off", - oldPodStatus: podWithoutObservedGen(), - podStatus: podWithObservedGenInConditions(), - featureGateOn: false, - wantPodStatus: podWithoutObservedGen(), - }, { name: "old=without, new=withInConditions / feature gate on", oldPodStatus: podWithoutObservedGen(), podStatus: podWithObservedGenInConditions(), - featureGateOn: true, wantPodStatus: podWithObservedGenInConditions(), }, - { - name: "old=withInConditions, new=without / feature gate off", - oldPodStatus: podWithObservedGenInConditions(), - podStatus: podWithoutObservedGen(), - featureGateOn: false, - wantPodStatus: podWithoutObservedGen(), - }, { name: "old=withInConditions, new=without / feature gate on", oldPodStatus: podWithObservedGenInConditions(), podStatus: podWithoutObservedGen(), - featureGateOn: true, wantPodStatus: podWithoutObservedGen(), }, - { - name: "old=withInConditions, new=withInCondtions / feature gate off", - oldPodStatus: podWithObservedGenInConditions(), - podStatus: podWithObservedGenInConditions(), - featureGateOn: false, - wantPodStatus: podWithObservedGenInConditions(), - }, { name: "old=withInConditions, new=withInCondtions / feature gate on", oldPodStatus: podWithObservedGenInConditions(), podStatus: podWithObservedGenInConditions(), - featureGateOn: true, wantPodStatus: podWithObservedGenInConditions(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodObservedGenerationTracking, tt.featureGateOn) dropDisabledPodStatusFields(tt.podStatus, tt.oldPodStatus, &api.PodSpec{}, &api.PodSpec{}) if !reflect.DeepEqual(tt.podStatus, tt.wantPodStatus) { t.Errorf("dropDisabledStatusFields() = %v, want %v", tt.podStatus, tt.wantPodStatus) @@ -2790,8 +2745,10 @@ func TestOldPodViolatesMatchLabelKeysValidationOption(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tc.matchLabelKeysEnabled) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpreadSelectorMerge, tc.matchLabelKeysSelectorMergeEnabled) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.MatchLabelKeysInPodTopologySpread: tc.matchLabelKeysEnabled, + features.MatchLabelKeysInPodTopologySpreadSelectorMerge: tc.matchLabelKeysSelectorMergeEnabled, + }) gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil) if tc.wantOption != gotOptions.OldPodViolatesMatchLabelKeysValidation { t.Errorf("Got OldPodViolatesMatchLabelKeysValidation=%t, want %t", gotOptions.OldPodViolatesMatchLabelKeysValidation, tc.wantOption) @@ -2851,8 +2808,10 @@ func TestOldPodViolatesLegacyMatchLabelKeysValidationOption(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tc.matchLabelKeysEnabled) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpreadSelectorMerge, tc.matchLabelKeysSelectorMergeEnabled) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.MatchLabelKeysInPodTopologySpread: tc.matchLabelKeysEnabled, + features.MatchLabelKeysInPodTopologySpreadSelectorMerge: tc.matchLabelKeysSelectorMergeEnabled, + }) gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil) if tc.wantOption != gotOptions.OldPodViolatesLegacyMatchLabelKeysValidation { t.Errorf("Got OldPodViolatesLegacyMatchLabelKeysValidation=%t, want %t", gotOptions.OldPodViolatesLegacyMatchLabelKeysValidation, tc.wantOption) @@ -2943,6 +2902,7 @@ func TestValidateAllowNonLocalProjectedTokenPathOption(t *testing.T) { } func TestDropInPlacePodVerticalScaling(t *testing.T) { + featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34")) podWithInPlaceVerticalScaling := func() *api.Pod { return &api.Pod{ Spec: api.PodSpec{ @@ -3331,6 +3291,8 @@ func TestDropSidecarContainers(t *testing.T) { } func TestDropClusterTrustBundleProjectedVolumes(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ClusterTrustBundle, true) + testCases := []struct { description string clusterTrustBundleProjectionEnabled bool @@ -4102,6 +4064,8 @@ func TestDropSupplementalGroupsPolicy(t *testing.T) { "feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description, ), func(t *testing.T) { + // Set emulation version so that the feature gate can be disabled in the test + featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34")) featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SupplementalGroupsPolicy, enabled) var oldPodSpec *api.PodSpec @@ -4442,6 +4406,7 @@ func TestDropSELinuxChangePolicy(t *testing.T) { } func TestValidateAllowSidecarResizePolicy(t *testing.T) { + featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34")) restartPolicyAlways := api.ContainerRestartPolicyAlways testCases := []struct { name string @@ -6253,3 +6218,519 @@ func TestHasUserNamespacesWithVolumeDevices(t *testing.T) { }) } } + +func TestTaintTolerationComparisonOperatorsInUse(t *testing.T) { + tests := []struct { + name string + podSpec *api.PodSpec + expected bool + }{ + { + name: "nil pod spec", + podSpec: nil, + expected: false, + }, + { + name: "no tolerations", + podSpec: &api.PodSpec{ + Containers: []api.Container{{Name: "test"}}, + }, + expected: false, + }, + { + name: "only Equal operator", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpEqual, Value: "value1"}, + }, + }, + expected: false, + }, + { + name: "only Exists operator", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpExists}, + }, + }, + expected: false, + }, + { + name: "Lt operator present", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpLt, Value: "100"}, + }, + }, + expected: true, + }, + { + name: "Gt operator present", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpGt, Value: "50"}, + }, + }, + expected: true, + }, + { + name: "mixed operators with Lt", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpEqual, Value: "value1"}, + {Key: "key2", Operator: api.TolerationOpLt, Value: "100"}, + {Key: "key3", Operator: api.TolerationOpExists}, + }, + }, + expected: true, + }, + { + name: "mixed operators with Gt", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpExists}, + {Key: "key2", Operator: api.TolerationOpGt, Value: "200"}, + }, + }, + expected: true, + }, + { + name: "both Lt and Gt operators", + podSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpLt, Value: "100"}, + {Key: "key2", Operator: api.TolerationOpGt, Value: "50"}, + }, + }, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := taintTolerationComparisonOperatorsInUse(test.podSpec) + if test.expected != actual { + t.Errorf("expected %v, got %v", test.expected, actual) + } + }) + } +} + +func TestAllowTaintTolerationComparisonOperators(t *testing.T) { + tests := []struct { + name string + featureEnabled bool + oldPodSpec *api.PodSpec + expected bool + }{ + { + name: "feature gate enabled, nil old pod spec", + featureEnabled: true, + oldPodSpec: nil, + expected: true, + }, + { + name: "feature gate enabled, old pod spec without comparison operators", + featureEnabled: true, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpEqual, Value: "value1"}, + }, + }, + expected: true, + }, + { + name: "feature gate enabled, old pod spec with Lt operator", + featureEnabled: true, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpLt, Value: "100"}, + }, + }, + expected: true, + }, + { + name: "feature gate disabled, nil old pod spec", + featureEnabled: false, + oldPodSpec: nil, + expected: false, + }, + { + name: "feature gate disabled, old pod spec without comparison operators", + featureEnabled: false, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpEqual, Value: "value1"}, + {Key: "key2", Operator: api.TolerationOpExists}, + }, + }, + expected: false, + }, + { + name: "feature gate disabled, old pod spec with Lt operator", + featureEnabled: false, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpLt, Value: "100"}, + }, + }, + expected: true, + }, + { + name: "feature gate disabled, old pod spec with Gt operator", + featureEnabled: false, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpGt, Value: "50"}, + }, + }, + expected: true, + }, + { + name: "feature gate disabled, old pod spec with mixed operators including Lt", + featureEnabled: false, + oldPodSpec: &api.PodSpec{ + Tolerations: []api.Toleration{ + {Key: "key1", Operator: api.TolerationOpEqual, Value: "value1"}, + {Key: "key2", Operator: api.TolerationOpLt, Value: "100"}, + {Key: "key3", Operator: api.TolerationOpExists}, + }, + }, + expected: true, + }, + { + name: "feature gate disabled, empty old pod spec", + featureEnabled: false, + oldPodSpec: &api.PodSpec{}, + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintTolerationComparisonOperators, test.featureEnabled) + actual := allowTaintTolerationComparisonOperators(test.oldPodSpec) + if test.expected != actual { + t.Errorf("expected %v, got %v", test.expected, actual) + } + }) + } +} + +func TestDisabledWorkload(t *testing.T) { + podWithWorkload := &api.Pod{ + Spec: api.PodSpec{ + WorkloadRef: &api.WorkloadReference{ + Name: "w", + PodGroup: "pg", + }, + }, + } + podWithoutWorkload := &api.Pod{ + Spec: api.PodSpec{}, + } + + tests := []struct { + name string + enabled bool + oldPod *api.Pod + newPod *api.Pod + wantPod *api.Pod + }{ + { + name: "old with workload / new with workload / disabled", + oldPod: podWithWorkload, + newPod: podWithWorkload, + wantPod: podWithWorkload, + }, + { + name: "old without workload / new with workload / disabled", + oldPod: podWithoutWorkload, + newPod: podWithWorkload, + wantPod: podWithoutWorkload, + }, + { + name: "old with workload / new without workload / disabled", + oldPod: podWithWorkload, + newPod: podWithoutWorkload, + wantPod: podWithoutWorkload, + }, + { + name: "old without workload / new without workload / disabled", + oldPod: podWithoutWorkload, + newPod: podWithoutWorkload, + wantPod: podWithoutWorkload, + }, + { + name: "old with workload / new with workload / enabled", + enabled: true, + oldPod: podWithWorkload, + newPod: podWithWorkload, + wantPod: podWithWorkload, + }, + { + name: "old without workload / new with workload / enabled", + enabled: true, + oldPod: podWithoutWorkload, + newPod: podWithWorkload, + wantPod: podWithWorkload, + }, + { + name: "old with workload / new without workload / enabled", + enabled: true, + oldPod: podWithWorkload, + newPod: podWithoutWorkload, + wantPod: podWithoutWorkload, + }, + { + name: "old without workload / new without workload / enabled", + enabled: true, + oldPod: podWithoutWorkload, + newPod: podWithoutWorkload, + wantPod: podWithoutWorkload, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.GenericWorkload, tc.enabled) + + oldPod := tc.oldPod.DeepCopy() + newPod := tc.newPod.DeepCopy() + wantPod := tc.wantPod + DropDisabledPodFields(newPod, oldPod) + + // Old pod should be never changed + if diff := cmp.Diff(oldPod, tc.oldPod); diff != "" { + t.Errorf("Old pod changed (-want,+got): %s", diff) + } + + if diff := cmp.Diff(wantPod, newPod); diff != "" { + t.Errorf("New pod changed (-want,+got): %s", diff) + } + }) + } +} + +func TestValidateRestartAllContainersOption(t *testing.T) { + policyAlways := api.ContainerRestartPolicyAlways + testCases := []struct { + name string + oldPodSpec *api.PodSpec + featureEnabled bool + want bool + }{ + { + name: "feature enabled", + featureEnabled: true, + want: true, + }, + { + name: "feature disabled", + featureEnabled: false, + want: false, + }, + { + name: "old pod spec has container without action", + oldPodSpec: &api.PodSpec{ + Containers: []api.Container{{ + Name: "container", + }}, + }, + featureEnabled: false, + want: false, + }, + { + name: "old pod spec has container with action", + oldPodSpec: &api.PodSpec{ + Containers: []api.Container{{ + Name: "container", + RestartPolicyRules: []api.ContainerRestartRule{{ + Action: api.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &api.ContainerRestartRuleOnExitCodes{ + Operator: api.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }}, + }}, + }, + featureEnabled: false, + want: true, + }, + { + name: "old pod spec has sidecar containers with rules", + oldPodSpec: &api.PodSpec{ + InitContainers: []api.Container{{ + RestartPolicy: &policyAlways, + RestartPolicyRules: []api.ContainerRestartRule{{ + Action: api.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &api.ContainerRestartRuleOnExitCodes{ + Operator: api.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }}, + }}, + }, + featureEnabled: false, + want: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.ContainerRestartRules: tc.featureEnabled, + features.NodeDeclaredFeatures: tc.featureEnabled, + features.RestartAllContainersOnContainerExits: tc.featureEnabled, + }) + // The new pod doesn't impact the outcome. + gotOptions := GetValidationOptionsFromPodSpecAndMeta(nil, tc.oldPodSpec, nil, nil) + if tc.want != gotOptions.AllowRestartAllContainers { + t.Errorf("unexpected diff, want: %v, got: %v", tc.want, gotOptions.AllowRestartAllContainers) + } + }) + } +} + +func TestDropDisabledPodStatusFields_InPlacePodLevelResourcesVerticalScaling(t *testing.T) { + testCases := []struct { + description string + hasInPlacePodLevelResourcesVerticalScaling bool + pod func() *api.Pod + }{ + { + description: "without pod-level status resources", + hasInPlacePodLevelResourcesVerticalScaling: false, + pod: func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "c1", + Image: "image", + Resources: api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + }, + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + {ResourceName: api.ResourceMemory, RestartPolicy: api.RestartContainer}, + }, + }, + }, + }, + Status: api.PodStatus{ + Resize: api.PodResizeStatusInProgress, + ContainerStatuses: []api.ContainerStatus{ + { + Name: "c1", + Image: "image", + AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")}, + }, + }, + }, + }, + } + }, + }, + { + description: "with pod-level status resources", + hasInPlacePodLevelResourcesVerticalScaling: true, + pod: func() *api.Pod { + return &api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "c1", + Image: "image", + Resources: api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + }, + ResizePolicy: []api.ContainerResizePolicy{ + {ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired}, + {ResourceName: api.ResourceMemory, RestartPolicy: api.RestartContainer}, + }, + }, + }, + }, + Status: api.PodStatus{ + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")}, + }, + AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Resize: api.PodResizeStatusInProgress, + ContainerStatuses: []api.ContainerStatus{ + { + Name: "c1", + Image: "image", + AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")}, + Resources: &api.ResourceRequirements{ + Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")}, + Limits: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")}, + }, + }, + }, + }, + } + }, + }, + { + description: "is nil", + hasInPlacePodLevelResourcesVerticalScaling: false, + pod: func() *api.Pod { return nil }, + }, + } + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeDeclaredFeatures, true) + for _, ippvsEnabled := range []bool{true, false} { + t.Run(fmt.Sprintf("InPlacePodLevelResourcesVerticalScaling=%t", ippvsEnabled), func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodLevelResourcesVerticalScaling, ippvsEnabled) + for _, oldPodInfo := range testCases { + for _, newPodInfo := range testCases { + oldPodHasInPlacePodLevelResourcesVerticalScaling, oldPod := oldPodInfo.hasInPlacePodLevelResourcesVerticalScaling, oldPodInfo.pod() + newPodHasInPlacePodLevelResourcesVerticalScaling, newPod := newPodInfo.hasInPlacePodLevelResourcesVerticalScaling, newPodInfo.pod() + if newPod == nil { + continue + } + t.Run(fmt.Sprintf("old pod %v, new pod %v", oldPodInfo.description, newPodInfo.description), func(t *testing.T) { + var oldPodSpec *api.PodSpec + var oldPodStatus *api.PodStatus + if oldPod != nil { + oldPodSpec = &oldPod.Spec + oldPodStatus = &oldPod.Status + } + dropDisabledPodStatusFields(&newPod.Status, oldPodStatus, &newPod.Spec, oldPodSpec) + + // old pod should never be changed + if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) { + t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod())) + } + + switch { + case ippvsEnabled || oldPodHasInPlacePodLevelResourcesVerticalScaling: + // new pod shouldn't change if feature enabled + expected := newPodInfo.pod() + if !reflect.DeepEqual(newPod, expected) { + t.Errorf("new pod changed: %v %t", cmp.Diff(newPod, expected), ippvsEnabled) + } + case newPodHasInPlacePodLevelResourcesVerticalScaling: + // new pod should be changed + if reflect.DeepEqual(newPod, newPodInfo.pod()) { + t.Errorf("new pod was not changed") + } + default: + // new pod should not need to be changed + if !reflect.DeepEqual(newPod, newPodInfo.pod()) { + t.Errorf("new pod changed: %v %t", cmp.Diff(newPod, newPodInfo.pod()), newPodHasInPlacePodLevelResourcesVerticalScaling) + } + } + }) + } + } + }) + } +} diff --git a/pkg/api/resourceclaimspec/util.go b/pkg/api/resourceclaimspec/util.go new file mode 100644 index 0000000000000..3c66d3dbeb28e --- /dev/null +++ b/pkg/api/resourceclaimspec/util.go @@ -0,0 +1,177 @@ +/* +Copyright 2025 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 resourceclaimspec + +import ( + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/kubernetes/pkg/features" +) + +// DropDisabledFields removes disabled fields from the spec unless they were in +// use there before. +// +// Theoretically some features could be in use in an old ResourceClaim status +// and not in use in the spec. This can only occur in a spec update, which is +// currently prevented because the entire spec is immutable. Even if it was +// allowed, preventing adding disabled fields to the spec is the right thing to +// do regardless of what may have ended up in the status earlier. +func DropDisabledFields(new, old *resource.ResourceClaimSpec) { + dropDisabledDRAPrioritizedListFields(new, old) + dropDisabledDRADeviceTaintsFields(new, old) // Intentionally after dropDisabledDRAPrioritizedListFields to avoid iterating over FirstAvailable slice which needs to be dropped. + dropDisabledDRAAdminAccessFields(new, old) + dropDisabledDRAResourceClaimConsumableCapacityFields(new, old) +} + +func dropDisabledDRADeviceTaintsFields(new, old *resource.ResourceClaimSpec) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRADeviceTaints) || + draDeviceTaintsInUse(old) { + return + } + + for i, req := range new.Devices.Requests { + if exactly := req.Exactly; exactly != nil { + exactly.Tolerations = nil + } + for e := range req.FirstAvailable { + new.Devices.Requests[i].FirstAvailable[e].Tolerations = nil + } + } +} + +func draDeviceTaintsInUse(spec *resource.ResourceClaimSpec) bool { + if spec == nil { + return false + } + + for _, req := range spec.Devices.Requests { + if exactly := req.Exactly; exactly != nil && len(exactly.Tolerations) > 0 { + return true + } + for _, sub := range req.FirstAvailable { + if len(sub.Tolerations) > 0 { + return true + } + } + } + + return false +} + +func dropDisabledDRAPrioritizedListFields(new, old *resource.ResourceClaimSpec) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAPrioritizedList) { + return + } + if draPrioritizedListFeatureInUse(old) { + return + } + + for i := range new.Devices.Requests { + new.Devices.Requests[i].FirstAvailable = nil + } +} + +func draPrioritizedListFeatureInUse(spec *resource.ResourceClaimSpec) bool { + if spec == nil { + return false + } + + for _, request := range spec.Devices.Requests { + if len(request.FirstAvailable) > 0 { + return true + } + } + + return false +} + +func dropDisabledDRAAdminAccessFields(new, old *resource.ResourceClaimSpec) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess) || + DRAAdminAccessFeatureInUse(old) { + // No need to drop anything. + return + } + + for i := range new.Devices.Requests { + if new.Devices.Requests[i].Exactly != nil { + new.Devices.Requests[i].Exactly.AdminAccess = nil + } + } +} + +// DRAAdminAccessFeatureInUse checks whether the feature is in use in the spec. +func DRAAdminAccessFeatureInUse(spec *resource.ResourceClaimSpec) bool { + if spec == nil { + return false + } + + for _, request := range spec.Devices.Requests { + if request.Exactly != nil && request.Exactly.AdminAccess != nil { + return true + } + } + + return false +} + +func dropDisabledDRAResourceClaimConsumableCapacityFields(new, old *resource.ResourceClaimSpec) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAConsumableCapacity) || + DRAConsumableCapacityFeatureInUse(old) { + // No need to drop anything. + return + } + + for i := range new.Devices.Constraints { + new.Devices.Constraints[i].DistinctAttribute = nil + } + + for i := range new.Devices.Requests { + if new.Devices.Requests[i].Exactly != nil { + new.Devices.Requests[i].Exactly.Capacity = nil + } + request := new.Devices.Requests[i] + for j := range request.FirstAvailable { + new.Devices.Requests[i].FirstAvailable[j].Capacity = nil + } + } +} + +// DRAConsumableCapacityFeatureInUse checks whether the feature is in use in the spec. +func DRAConsumableCapacityFeatureInUse(spec *resource.ResourceClaimSpec) bool { + if spec == nil { + return false + } + + for _, constaint := range spec.Devices.Constraints { + if constaint.DistinctAttribute != nil { + return true + } + } + + for _, request := range spec.Devices.Requests { + if request.Exactly != nil && request.Exactly.Capacity != nil { + return true + } + for _, subRequest := range request.FirstAvailable { + if subRequest.Capacity != nil { + return true + } + } + } + + return false +} diff --git a/pkg/api/resourceclaimspec/util_test.go b/pkg/api/resourceclaimspec/util_test.go new file mode 100644 index 0000000000000..d1f69d425da93 --- /dev/null +++ b/pkg/api/resourceclaimspec/util_test.go @@ -0,0 +1,149 @@ +/* +Copyright 2025 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 resourceclaimspec + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + apiresource "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/utils/ptr" +) + +var testCapacity = map[resource.QualifiedName]apiresource.Quantity{ + resource.QualifiedName("test-capacity"): apiresource.MustParse("1"), +} + +var obj = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "kube-system", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + Exactly: &resource.ExactDeviceRequest{ + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + }, + }, + }, + }, + }, +} + +var objWithPrioritizedList = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "kube-system", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + FirstAvailable: []resource.DeviceSubRequest{ + { + Name: "subreq-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeExactCount, + Count: 1, + }, + }, + }, + }, + }, + }, +} + +func modifySpecDeviceRequestWithCapacityRequests(resourceClaim *resource.ResourceClaim, + capacity map[resource.QualifiedName]apiresource.Quantity, prioritizedListFeature bool) { + if capacity != nil { + if prioritizedListFeature { + resourceClaim.Spec.Devices.Requests[0].FirstAvailable[0].Capacity = &resource.CapacityRequirements{ + Requests: capacity, + } + } else { + resourceClaim.Spec.Devices.Requests[0].Exactly.Capacity = &resource.CapacityRequirements{ + Requests: capacity, + } + } + } +} + +func addDistinctAttribute(resourceClaim *resource.ResourceClaim) { + distinctConstraint := resource.DeviceConstraint{ + Requests: []string{"req-0"}, + DistinctAttribute: ptr.To(resource.FullyQualifiedName("driver-a/attr")), + } + resourceClaim.Spec.Devices.Constraints = append(resourceClaim.Spec.Devices.Constraints, distinctConstraint) +} + +func TestDRAConsumableCapacityFeatureInUse(t *testing.T) { + testcases := map[string]struct { + obj *resource.ResourceClaim + expect bool + }{ + "consumable-capacity-empty": { + obj: nil, + expect: false, + }, + "consumable-capacity-no-inuse": { + obj: obj, + expect: false, + }, + "consumable-capacity-with-inuse-fields": { + obj: func() *resource.ResourceClaim { + obj := obj.DeepCopy() + modifySpecDeviceRequestWithCapacityRequests(obj, testCapacity, false) + return obj + }(), + expect: true, + }, + "consumable-capacity--with-inuse-fields-with-distinct-attribute": { + obj: func() *resource.ResourceClaim { + obj := obj.DeepCopy() + modifySpecDeviceRequestWithCapacityRequests(obj, testCapacity, false) + addDistinctAttribute(obj) + return obj + }(), + expect: true, + }, + "consumable-capacity--with-inuse-fields-in-subrequests": { + obj: func() *resource.ResourceClaim { + obj := objWithPrioritizedList.DeepCopy() + modifySpecDeviceRequestWithCapacityRequests(obj, testCapacity, true) + return obj + }(), + expect: true, + }, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + var spec *resource.ResourceClaimSpec + if tc.obj != nil { + spec = &tc.obj.Spec + } + assert.Equal(t, DRAConsumableCapacityFeatureInUse(spec), tc.expect) + }) + } +} diff --git a/pkg/api/service/warnings.go b/pkg/api/service/warnings.go index 92fef3afa90dd..a9f0cdad25f0b 100644 --- a/pkg/api/service/warnings.go +++ b/pkg/api/service/warnings.go @@ -72,6 +72,10 @@ func GetWarningsForService(service, oldService *api.Service) []string { warnings = append(warnings, fmt.Sprintf("spec.externalName is ignored when spec.type is not %q", api.ServiceTypeExternalName)) } + if service.Spec.TrafficDistribution != nil && *service.Spec.TrafficDistribution == api.ServiceTrafficDistributionPreferClose { + warnings = append(warnings, fmt.Sprintf("spec.trafficDistribution: %q is deprecated; use %q", api.ServiceTrafficDistributionPreferClose, api.ServiceTrafficDistributionPreferSameZone)) + } + return warnings } diff --git a/pkg/api/service/warnings_test.go b/pkg/api/service/warnings_test.go index dcf2a2ade19da..3fe97f7d7cb1b 100644 --- a/pkg/api/service/warnings_test.go +++ b/pkg/api/service/warnings_test.go @@ -23,6 +23,7 @@ import ( "github.com/google/go-cmp/cmp" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/utils/ptr" ) func TestGetWarningsForService(t *testing.T) { @@ -90,18 +91,31 @@ func TestGetWarningsForService(t *testing.T) { s.Spec.SessionAffinity = api.ServiceAffinityNone }, numWarnings: 0, - }, - { - name: "ExternalIPs, LoadBalancerIP and SessionAffinity set when headless service", - tweakSvc: func(s *api.Service) { - s.Spec.Type = api.ServiceTypeClusterIP - s.Spec.ClusterIP = api.ClusterIPNone - s.Spec.ExternalIPs = []string{"1.2.3.4"} - s.Spec.LoadBalancerIP = "1.2.3.4" - s.Spec.SessionAffinity = api.ServiceAffinityClientIP - }, - numWarnings: 3, - }} + }, { + name: "ExternalIPs, LoadBalancerIP and SessionAffinity set when headless service", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeClusterIP + s.Spec.ClusterIP = api.ClusterIPNone + s.Spec.ExternalIPs = []string{"1.2.3.4"} + s.Spec.LoadBalancerIP = "1.2.3.4" + s.Spec.SessionAffinity = api.ServiceAffinityClientIP + }, + numWarnings: 3, + }, { + name: "trafficDistribution: PreferSameZone", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeClusterIP + s.Spec.TrafficDistribution = ptr.To(api.ServiceTrafficDistributionPreferSameZone) + }, + numWarnings: 0, + }, { + name: "trafficDistribution: PreferClose", + tweakSvc: func(s *api.Service) { + s.Spec.Type = api.ServiceTypeClusterIP + s.Spec.TrafficDistribution = ptr.To(api.ServiceTrafficDistributionPreferClose) + }, + numWarnings: 1, + }} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/api/testing/OWNERS b/pkg/api/testing/OWNERS index 9e252305ad200..23e6246b1d473 100644 --- a/pkg/api/testing/OWNERS +++ b/pkg/api/testing/OWNERS @@ -1,5 +1,7 @@ # See the OWNERS docs at https://go.k8s.io/owners +approvers: + - jpbetz reviewers: - thockin - smarterclayton @@ -16,3 +18,7 @@ reviewers: - soltysh - jsafrane - rata + - jpbetz + - aaron-prindle + - lalitc375 + - yongruilin diff --git a/pkg/api/testing/defaulting_test.go b/pkg/api/testing/defaulting_test.go index 73cee1fa4c22a..6805a5c76b139 100644 --- a/pkg/api/testing/defaulting_test.go +++ b/pkg/api/testing/defaulting_test.go @@ -93,8 +93,8 @@ func TestDefaulting(t *testing.T) { {Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {}, {Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {}, {Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {}, - {Group: "certificates.k8s.io", Version: "v1alpha1", Kind: "PodCertificateRequest"}: {}, - {Group: "certificates.k8s.io", Version: "v1alpha1", Kind: "PodCertificateRequestList"}: {}, + {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "PodCertificateRequest"}: {}, + {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "PodCertificateRequestList"}: {}, {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequest"}: {}, {Group: "certificates.k8s.io", Version: "v1beta1", Kind: "CertificateSigningRequestList"}: {}, {Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSlice"}: {}, diff --git a/pkg/api/testing/serialization_proto_test.go b/pkg/api/testing/serialization_proto_test.go index 6ba4a47e7406c..7cc03b9faffab 100644 --- a/pkg/api/testing/serialization_proto_test.go +++ b/pkg/api/testing/serialization_proto_test.go @@ -24,7 +24,6 @@ import ( "reflect" "testing" - "github.com/gogo/protobuf/proto" "github.com/google/go-cmp/cmp" v1 "k8s.io/api/core/v1" @@ -225,7 +224,7 @@ func BenchmarkDecodeCodecToInternalProtobuf(b *testing.B) { b.StopTimer() } -// BenchmarkDecodeJSON provides a baseline for regular JSON decode performance +// BenchmarkDecodeIntoProtobuf provides a baseline for regular protobuf decode performance func BenchmarkDecodeIntoProtobuf(b *testing.B) { items := benchmarkItems(b) width := len(items) @@ -237,14 +236,16 @@ func BenchmarkDecodeIntoProtobuf(b *testing.B) { } encoded[i] = data validate := &v1.Pod{} - if err := proto.Unmarshal(data, validate); err != nil { + validate.Reset() // decode normally resets internally + if err := validate.Unmarshal(data); err != nil { b.Fatalf("Failed to unmarshal %d: %v\n%#v", i, err, items[i]) } } for i := 0; i < b.N; i++ { - obj := v1.Pod{} - if err := proto.Unmarshal(encoded[i%width], &obj); err != nil { + obj := &v1.Pod{} + obj.Reset() // decode normally resets internally + if err := obj.Unmarshal(encoded[i%width]); err != nil { b.Fatal(err) } } diff --git a/pkg/api/testing/validation.go b/pkg/api/testing/validation.go index ecf635f20d89d..e11ca144808c6 100644 --- a/pkg/api/testing/validation.go +++ b/pkg/api/testing/validation.go @@ -18,24 +18,65 @@ package testing import ( "bytes" + "context" "sort" "strconv" "testing" - k8sruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" runtimetest "k8s.io/apimachinery/pkg/runtime/testing" "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apiserver/pkg/features" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" ) +// ValidateFunc is a function that runs validation. +type ValidateFunc func(ctx context.Context, obj runtime.Object) field.ErrorList + +// ValidateUpdateFunc is a function that runs update validation. +type ValidateUpdateFunc func(ctx context.Context, obj, old runtime.Object) field.ErrorList + // VerifyVersionedValidationEquivalence tests that all versions of an API return equivalent validation errors. -func VerifyVersionedValidationEquivalence(t *testing.T, obj, old k8sruntime.Object, subresources ...string) { +// It accepts optional configuration to handle path normalization across API versions where structures differ. +func VerifyVersionedValidationEquivalence(t *testing.T, obj, old runtime.Object, testConfigs ...ValidationTestConfig) { t.Helper() + opts := &validationOption{} + for _, testcfg := range testConfigs { + testcfg(opts) + } + // Accumulate errors from all versioned validation, per version. all := map[string]field.ErrorList{} accumulate := func(t *testing.T, gv string, errs field.ErrorList) { + // If normalization rules are provided, apply them to the field paths of generated errors. + // This allows comparing errors between API versions that have structural differences + // (e.g. flattened vs nested fields). + // We must normalize in place before sorting. + for i := range errs { + currentPath := errs[i].Field + for _, rule := range opts.NormalizationRules { + normalized := rule.Regexp.ReplaceAllString(currentPath, rule.Replacement) + if normalized != currentPath { + errs[i].Field = normalized + // Apply only the first matching rule per error + break + } + } + } + // Re-sort the error list based primarily on the normalized field paths + // to ensure errors align correctly during index-by-index comparison, + // regardless of their original structure. + sort.Slice(errs, func(i, j int) bool { + if errs[i].Field != errs[j].Field { + return errs[i].Field < errs[j].Field + } + // Secondary sort by full error string for determinism when fields are equal + return errs[i].Error() < errs[j].Error() + }) all[gv] = errs } // Convert versioned object to internal format before validation. @@ -48,7 +89,7 @@ func VerifyVersionedValidationEquivalence(t *testing.T, obj, old k8sruntime.Obje return } if old == nil { - runtimetest.RunValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, accumulate, subresources...) + runtimetest.RunValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, accumulate, opts.SubResources...) } else { // Convert old versioned object to internal format before validation. // runtimetest.RunUpdateValidationForEachVersion requires unversioned (internal) objects as input. @@ -59,7 +100,7 @@ func VerifyVersionedValidationEquivalence(t *testing.T, obj, old k8sruntime.Obje if internalOld == nil { return } - runtimetest.RunUpdateValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, internalOld, accumulate, subresources...) + runtimetest.RunUpdateValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, internalOld, accumulate, opts.SubResources...) } // Make a copy so we can modify it. @@ -94,13 +135,19 @@ func VerifyVersionedValidationEquivalence(t *testing.T, obj, old k8sruntime.Obje } next := false for i := range lv { - if l, r := lv[i], rv[i]; l.Type != r.Type || l.Detail != r.Detail { - t.Errorf("different errors\n%s: %v\n%s: %v", lk, fmtErrs(lv), rk, fmtErrs(rv)) + // We don't use reflect.DeepEqual here because unversioned and versioned + // validation might have different bad values (e.g. pointer vs value). + // We also don't use ErrorMatcher here because it doesn't handle re-sorting + // required after normalization in multi-error scenarios within this specific loop structure. + l, r := lv[i], rv[i] + // Compare field (already normalized), type, detail, and origin. + if l.Type != r.Type || l.Field != r.Field || l.Detail != r.Detail || l.Origin != r.Origin { + t.Errorf("different errors at index %d\n%s: %v\n%s: %v", i, lk, l.Error(), rk, r.Error()) next = true - break } } if next { + t.Errorf("complete error lists for context:\n%s: %v\n%s: %v", lk, fmtErrs(lv), rk, fmtErrs(rv)) continue } } @@ -117,14 +164,14 @@ func fmtErrs(errs field.ErrorList) string { } buf := bytes.Buffer{} for _, e := range errs { - buf.WriteString("\n") + buf.WriteString("\n\t") buf.WriteString(strconv.Quote(e.Error())) } return buf.String() } -func convertToInternal(t *testing.T, scheme *k8sruntime.Scheme, obj k8sruntime.Object) (k8sruntime.Object, error) { +func convertToInternal(t *testing.T, scheme *runtime.Scheme, obj runtime.Object) (runtime.Object, error) { t.Helper() gvks, _, err := scheme.ObjectKinds(obj) @@ -135,13 +182,164 @@ func convertToInternal(t *testing.T, scheme *k8sruntime.Scheme, obj k8sruntime.O t.Fatal("no GVKs found for object") } gvk := gvks[0] - if gvk.Version == k8sruntime.APIVersionInternal { + if gvk.Version == runtime.APIVersionInternal { return obj, nil } - gvk.Version = k8sruntime.APIVersionInternal + gvk.Version = runtime.APIVersionInternal if !scheme.Recognizes(gvk) { t.Logf("no internal object found for GroupKind %s", gvk.GroupKind().String()) return nil, nil } - return scheme.ConvertToVersion(obj, schema.GroupVersion{Group: gvk.Group, Version: k8sruntime.APIVersionInternal}) + return scheme.ConvertToVersion(obj, schema.GroupVersion{Group: gvk.Group, Version: runtime.APIVersionInternal}) +} + +type ValidationTestConfig func(*validationOption) + +// validationOptions encapsulates optional parameters for validation equivalence tests. +type validationOption struct { + // SubResources are the subresources to validate. + SubResources []string + // NormalizationRules are the rules to apply to field paths before comparison. + NormalizationRules []field.NormalizationRule +} + +func WithSubResources(subResources ...string) ValidationTestConfig { + return func(o *validationOption) { + o.SubResources = subResources + } +} + +func WithNormalizationRules(rules ...field.NormalizationRule) ValidationTestConfig { + return func(o *validationOption) { + o.NormalizationRules = rules + } +} + +// VerifyValidationEquivalence provides a helper for testing the migration from +// hand-written imperative validation to declarative validation. It ensures that +// the validation logic remains consistent before and after the feature is enabled. +// +// The function operates by running the provided validation function under two scenarios: +// 1. With DeclarativeValidation and DeclarativeValidationTakeover feature gates disabled, +// simulating the legacy hand-written validation. +// 2. With both feature gates enabled, using the new declarative validation rules. +// +// It then asserts that the validation errors produced in both scenarios are equivalent, +// guaranteeing a safe migration. It also checks the errors against an expected set. +// It compares errors by field, origin and type; all three should match to be called equivalent. +// It also make sure all versions of the given API returns equivalent errors. +func VerifyValidationEquivalence(t *testing.T, ctx context.Context, obj runtime.Object, validateFn ValidateFunc, expectedErrs field.ErrorList, testConfigs ...ValidationTestConfig) { + t.Helper() + opts := &validationOption{} + for _, testcfg := range testConfigs { + testcfg(opts) + } + verifyValidationEquivalence(t, expectedErrs, func() field.ErrorList { + return validateFn(ctx, obj) + }, opts) + VerifyVersionedValidationEquivalence(t, obj, nil, testConfigs...) +} + +// VerifyUpdateValidationEquivalence provides a helper for testing the migration from +// hand-written imperative validation to declarative validation for update operations. +// It ensures that the validation logic remains consistent before and after the feature is enabled. +// +// The function operates by running the provided validation function under two scenarios: +// 1. With DeclarativeValidation and DeclarativeValidationTakeover feature gates disabled, +// simulating the legacy hand-written validation. +// 2. With both feature gates enabled, using the new declarative validation rules. +// +// It then asserts that the validation errors produced in both scenarios are equivalent, +// guaranteeing a safe migration. It also checks the errors against an expected set. +// It compares errors by field, origin and type; all three should match to be called equivalent. +// It also make sure all versions of the given API returns equivalent errors. +func VerifyUpdateValidationEquivalence(t *testing.T, ctx context.Context, obj, old runtime.Object, validateUpdateFn ValidateUpdateFunc, expectedErrs field.ErrorList, testConfigs ...ValidationTestConfig) { + t.Helper() + opts := &validationOption{} + for _, testcfg := range testConfigs { + testcfg(opts) + } + verifyValidationEquivalence(t, expectedErrs, func() field.ErrorList { + return validateUpdateFn(ctx, obj, old) + }, opts) + VerifyVersionedValidationEquivalence(t, obj, old, testConfigs...) +} + +// verifyValidationEquivalence is a generic helper that verifies validation equivalence with and without declarative validation. +func verifyValidationEquivalence(t *testing.T, expectedErrs field.ErrorList, runValidations func() field.ErrorList, opt *validationOption) { + t.Helper() + var declarativeTakeoverErrs field.ErrorList + var imperativeErrs field.ErrorList + + // The errOutputMatcher is used to verify the output matches the expected errors in test cases. + errOutputMatcher := field.ErrorMatcher{}.ByType().ByOrigin().ByFieldNormalized(opt.NormalizationRules) + + // We only need to test both gate enabled and disabled together, because + // 1) the DeclarativeValidationTakeover won't take effect if DeclarativeValidation is disabled. + // 2) the validation output, when only DeclarativeValidation is enabled, is the same as when both gates are disabled. + t.Run("with declarative validation", func(t *testing.T) { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.DeclarativeValidation: true, + features.DeclarativeValidationTakeover: true, + }) + declarativeTakeoverErrs = runValidations() + + if len(expectedErrs) > 0 { + errOutputMatcher.Test(t, expectedErrs, declarativeTakeoverErrs) + } else if len(declarativeTakeoverErrs) != 0 { + t.Errorf("expected no errors, but got: %v", declarativeTakeoverErrs) + } + }) + + t.Run("hand written validation", func(t *testing.T) { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.DeclarativeValidationTakeover: false, + features.DeclarativeValidation: false, + }) + imperativeErrs = runValidations() + + if len(expectedErrs) > 0 { + errOutputMatcher.Test(t, expectedErrs, imperativeErrs) + } else if len(imperativeErrs) != 0 { + t.Errorf("expected no errors, but got: %v", imperativeErrs) + } + }) + + if t.Failed() { + // There is no point in moving forward, if any of above tests failed for any reason. Running follow up tests will return noise. + t.SkipNow() + } + + // The equivalenceMatcher is used to verify the output errors from hand-written imperative validation + // are equivalent to the output errors when DeclarativeValidationTakeover is enabled. + equivalenceMatcher := field.ErrorMatcher{}.ByType().ByOrigin() + if len(opt.NormalizationRules) > 0 { + equivalenceMatcher = equivalenceMatcher.ByFieldNormalized(opt.NormalizationRules) + } else { + equivalenceMatcher = equivalenceMatcher.ByField() + } + + // The imperative validation may produce duplicate errors, which is not supported by the ErrorMatcher. + // TODO: remove this once ErrorMatcher has been extended to handle this form of deduplication. + imperativeErrs = deDuplicateErrors(imperativeErrs, equivalenceMatcher) + + equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) +} + +// deDuplicateErrors removes duplicate errors from an ErrorList based on the provided matcher. +func deDuplicateErrors(errs field.ErrorList, matcher field.ErrorMatcher) field.ErrorList { + var deduped field.ErrorList + for _, err := range errs { + found := false + for _, existingErr := range deduped { + if matcher.Matches(existingErr, err) { + found = true + break + } + } + if !found { + deduped = append(deduped, err) + } + } + return deduped } diff --git a/pkg/api/testing/validation_test.go b/pkg/api/testing/validation_test.go index 922fb526f76c1..41ec078559180 100644 --- a/pkg/api/testing/validation_test.go +++ b/pkg/api/testing/validation_test.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/kubernetes/pkg/api/legacyscheme" + resourcevalidation "k8s.io/kubernetes/pkg/apis/resource/validation" ) // FIXME: Automatically finds all group/versions supporting declarative validation, or add @@ -35,28 +36,41 @@ func TestVersionedValidationByFuzzing(t *testing.T) { {Group: "certificates.k8s.io", Version: "v1"}, {Group: "certificates.k8s.io", Version: "v1alpha1"}, {Group: "certificates.k8s.io", Version: "v1beta1"}, + {Group: "resource.k8s.io", Version: "v1beta1"}, + {Group: "resource.k8s.io", Version: "v1beta2"}, + {Group: "resource.k8s.io", Version: "v1"}, + {Group: "storage.k8s.io", Version: "v1"}, + {Group: "storage.k8s.io", Version: "v1beta1"}, + {Group: "storage.k8s.io", Version: "v1alpha1"}, } + fuzzIters := *roundtrip.FuzzIters / 10 // TODO: Find a better way to manage test running time + f := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(rand.Int63()), legacyscheme.Codecs) + for _, gv := range typesWithDeclarativeValidation { - t.Run(gv.String(), func(t *testing.T) { - for i := 0; i < *roundtrip.FuzzIters; i++ { - f := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(rand.Int63()), legacyscheme.Codecs) - for kind := range legacyscheme.Scheme.KnownTypes(gv) { - obj, err := legacyscheme.Scheme.New(gv.WithKind(kind)) + for kind := range legacyscheme.Scheme.KnownTypes(gv) { + gvk := gv.WithKind(kind) + t.Run(gvk.String(), func(t *testing.T) { + for i := 0; i < fuzzIters; i++ { + obj, err := legacyscheme.Scheme.New(gvk) if err != nil { t.Fatalf("could not create a %v: %s", kind, err) } f.Fill(obj) - VerifyVersionedValidationEquivalence(t, obj, nil) + + var opts []ValidationTestConfig + opts = append(opts, WithNormalizationRules(resourcevalidation.ResourceNormalizationRules...)) + + VerifyVersionedValidationEquivalence(t, obj, nil, opts...) old, err := legacyscheme.Scheme.New(gv.WithKind(kind)) if err != nil { t.Fatalf("could not create a %v: %s", kind, err) } f.Fill(old) - VerifyVersionedValidationEquivalence(t, obj, old) + VerifyVersionedValidationEquivalence(t, obj, old, opts...) } - } - }) + }) + } } } diff --git a/pkg/api/v1/endpoints/util.go b/pkg/api/v1/endpoints/util.go index d0af83be2745d..f283230652eba 100644 --- a/pkg/api/v1/endpoints/util.go +++ b/pkg/api/v1/endpoints/util.go @@ -18,12 +18,12 @@ package endpoints import ( "bytes" - "crypto/md5" "encoding/hex" "hash" + "hash/fnv" "sort" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" hashutil "k8s.io/kubernetes/pkg/util/hash" ) @@ -154,7 +154,7 @@ func hashAddresses(addrs addressSet) string { slice = append(slice, addrReady{k, ready}) } sort.Sort(addrsReady(slice)) - hasher := md5.New() + hasher := fnv.New128a() hashutil.DeepHashObject(hasher, slice) return hex.EncodeToString(hasher.Sum(nil)[0:]) } @@ -210,7 +210,7 @@ type subsetsByHash []v1.EndpointSubset func (sl subsetsByHash) Len() int { return len(sl) } func (sl subsetsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] } func (sl subsetsByHash) Less(i, j int) bool { - hasher := md5.New() + hasher := fnv.New128a() h1 := hashObject(hasher, sl[i]) h2 := hashObject(hasher, sl[j]) return bytes.Compare(h1, h2) < 0 @@ -229,7 +229,7 @@ type portsByHash []v1.EndpointPort func (sl portsByHash) Len() int { return len(sl) } func (sl portsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] } func (sl portsByHash) Less(i, j int) bool { - hasher := md5.New() + hasher := fnv.New128a() h1 := hashObject(hasher, sl[i]) h2 := hashObject(hasher, sl[j]) return bytes.Compare(h1, h2) < 0 diff --git a/pkg/api/v1/pod/util.go b/pkg/api/v1/pod/util.go index 6d79457150cf7..6533b38ee890c 100644 --- a/pkg/api/v1/pod/util.go +++ b/pkg/api/v1/pod/util.go @@ -418,11 +418,16 @@ func IsContainerRestartable(pod v1.PodSpec, container v1.Container) bool { // level policy are specified, pod-level restart policy is used. func ContainerShouldRestart(container v1.Container, pod v1.PodSpec, exitCode int32) bool { if container.RestartPolicy != nil { - rule, ok := findMatchingContainerRestartRule(container, exitCode) + rule, ok := FindMatchingContainerRestartRule(container, exitCode) if ok { switch rule.Action { case v1.ContainerRestartRuleActionRestart: return true + case v1.ContainerRestartRuleActionRestartAllContainers: + if utilfeature.DefaultFeatureGate.Enabled(features.RestartAllContainersOnContainerExits) { + return true + } + // If feature is not enabled, fallback to container-level policy. default: // Do nothing, fallback to container-level restart policy. } @@ -454,10 +459,10 @@ func ContainerShouldRestart(container v1.Container, pod v1.PodSpec, exitCode int } } -// findMatchingContainerRestartRule returns a rule and true if the exitCode matched +// FindMatchingContainerRestartRule returns a rule and true if the exitCode matched // one of the restart rules for the given container. Returns and empty rule and // false if no rules matched. -func findMatchingContainerRestartRule(container v1.Container, exitCode int32) (rule v1.ContainerRestartRule, found bool) { +func FindMatchingContainerRestartRule(container v1.Container, exitCode int32) (rule v1.ContainerRestartRule, found bool) { for _, rule := range container.RestartPolicyRules { if rule.ExitCodes != nil { exitCodeMatched := false @@ -483,6 +488,30 @@ func findMatchingContainerRestartRule(container v1.Container, exitCode int32) (r return v1.ContainerRestartRule{}, false } +// AllContainersCouldRestart returns true if all containers could be restarted +// for the given pod. This is true if any container has a RestartAllContainers +// action. +func AllContainersCouldRestart(pod *v1.PodSpec) bool { + if pod == nil { + return false + } + for _, container := range pod.InitContainers { + for _, rule := range container.RestartPolicyRules { + if rule.Action == v1.ContainerRestartRuleActionRestartAllContainers { + return true + } + } + } + for _, container := range pod.Containers { + for _, rule := range container.RestartPolicyRules { + if rule.Action == v1.ContainerRestartRuleActionRestartAllContainers { + return true + } + } + } + return false +} + // CalculatePodStatusObservedGeneration calculates the observedGeneration for the pod status. // This is used to track the generation of the pod that was observed by the kubelet. // The observedGeneration is set to the pod's generation when the feature gate diff --git a/pkg/api/v1/pod/util_test.go b/pkg/api/v1/pod/util_test.go index 50c3e05461e5d..bd3044e195ef2 100644 --- a/pkg/api/v1/pod/util_test.go +++ b/pkg/api/v1/pod/util_test.go @@ -29,7 +29,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" - "k8s.io/component-base/featuregate" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/features" ) @@ -1015,21 +1014,8 @@ func TestCalculatePodStatusObservedGeneration(t *testing.T) { tests := []struct { name string pod *v1.Pod - features map[featuregate.Feature]bool expected int64 }{ - { - name: "pod with no observedGeneration/PodObservedGenerationTracking=false", - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 5, - }, - }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: false, - }, - expected: 0, - }, { name: "pod with no observedGeneration/PodObservedGenerationTracking=true", pod: &v1.Pod{ @@ -1037,24 +1023,6 @@ func TestCalculatePodStatusObservedGeneration(t *testing.T) { Generation: 5, }, }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: true, - }, - expected: 5, - }, - { - name: "pod with observedGeneration/PodObservedGenerationTracking=false", - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 5, - }, - Status: v1.PodStatus{ - ObservedGeneration: 5, - }, - }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: false, - }, expected: 5, }, { @@ -1067,18 +1035,12 @@ func TestCalculatePodStatusObservedGeneration(t *testing.T) { ObservedGeneration: 5, }, }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: true, - }, expected: 5, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - for f, v := range tc.features { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, v) - } assert.Equal(t, tc.expected, CalculatePodStatusObservedGeneration(tc.pod)) }) } @@ -1088,26 +1050,8 @@ func TestCalculatePodConditionObservedGeneration(t *testing.T) { tests := []struct { name string pod *v1.Pod - features map[featuregate.Feature]bool expected int64 }{ - { - name: "pod with no observedGeneration/PodObservedGenerationTracking=false", - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 5, - }, - Status: v1.PodStatus{ - Conditions: []v1.PodCondition{{ - Type: v1.PodReady, - }}, - }, - }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: false, - }, - expected: 0, - }, { name: "pod with no observedGeneration/PodObservedGenerationTracking=true", pod: &v1.Pod{ @@ -1120,27 +1064,6 @@ func TestCalculatePodConditionObservedGeneration(t *testing.T) { }}, }, }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: true, - }, - expected: 5, - }, - { - name: "pod with observedGeneration/PodObservedGenerationTracking=false", - pod: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Generation: 5, - }, - Status: v1.PodStatus{ - Conditions: []v1.PodCondition{{ - Type: v1.PodReady, - ObservedGeneration: 5, - }}, - }, - }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: false, - }, expected: 5, }, { @@ -1156,18 +1079,12 @@ func TestCalculatePodConditionObservedGeneration(t *testing.T) { }}, }, }, - features: map[featuregate.Feature]bool{ - features.PodObservedGenerationTracking: true, - }, expected: 5, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - for f, v := range tc.features { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, f, v) - } assert.Equal(t, tc.expected, CalculatePodConditionObservedGeneration(&tc.pod.Status, tc.pod.Generation, v1.PodReady)) }) } @@ -1268,6 +1185,7 @@ func TestContainerHasRestartablePolicy(t *testing.T) { name string container v1.Container podSpec v1.PodSpec + podStatus v1.PodStatus exitCode int32 expected bool }{ @@ -1305,6 +1223,23 @@ func TestContainerHasRestartablePolicy(t *testing.T) { exitCode: 99, expected: true, }, + { + name: "Rule: 'In' operator matches with 'RestartAllContainers' action", + container: v1.Container{ + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []v1.ContainerRestartRule{ + { + Action: v1.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &v1.ContainerRestartRuleOnExitCodes{ + Operator: v1.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42, 50, 60}, + }, + }, + }, + }, + exitCode: 42, + expected: true, + }, { name: "Rule: 'In' operator does not match, should fall back to container policy", container: v1.Container{ @@ -1397,6 +1332,11 @@ func TestContainerHasRestartablePolicy(t *testing.T) { expected: true, }, } + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.ContainerRestartRules: true, + features.NodeDeclaredFeatures: true, + features.RestartAllContainersOnContainerExits: true, + }) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -1411,6 +1351,90 @@ func TestContainerHasRestartablePolicy(t *testing.T) { } } +func TestAllContainersCouldRestart(t *testing.T) { + restartPolicyAlways := v1.ContainerRestartPolicyAlways + + testCases := []struct { + name string + podSpec *v1.PodSpec + expected bool + }{ + { + name: "Pod with rules in init containers", + podSpec: &v1.PodSpec{ + InitContainers: []v1.Container{ + { + RestartPolicyRules: []v1.ContainerRestartRule{ + { + Action: v1.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &v1.ContainerRestartRuleOnExitCodes{ + Operator: v1.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{1}, + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "Pod with rules in sidecar containers", + podSpec: &v1.PodSpec{ + InitContainers: []v1.Container{ + { + RestartPolicy: &restartPolicyAlways, + RestartPolicyRules: []v1.ContainerRestartRule{ + { + Action: v1.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &v1.ContainerRestartRuleOnExitCodes{ + Operator: v1.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{1}, + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "Pod with rules in regular containers", + podSpec: &v1.PodSpec{ + Containers: []v1.Container{ + { + RestartPolicy: &restartPolicyAlways, + RestartPolicyRules: []v1.ContainerRestartRule{ + { + Action: v1.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &v1.ContainerRestartRuleOnExitCodes{ + Operator: v1.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{1}, + }, + }, + }, + }, + }, + }, + expected: true, + }, + { + name: "Pod without rules", + podSpec: &v1.PodSpec{}, + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := AllContainersCouldRestart(tc.podSpec) + if got != tc.expected { + t.Errorf("AllContainersRestarting() = %v, want %v", got, tc.expected) + } + }) + } +} + func TestFindMatchingContainerRestartRule(t *testing.T) { ruleIn := v1.ContainerRestartRule{ Action: v1.ContainerRestartRuleActionRestart, @@ -1489,7 +1513,7 @@ func TestFindMatchingContainerRestartRule(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - rule, found := findMatchingContainerRestartRule(tc.container, tc.exitCode) + rule, found := FindMatchingContainerRestartRule(tc.container, tc.exitCode) if found != tc.expectedFound { t.Errorf("FindMatchingContainerRestartRule() found = %v, want %v", found, tc.expectedFound) } diff --git a/pkg/apis/abac/v1beta1/doc.go b/pkg/apis/abac/v1beta1/doc.go index 07647ddc3575a..f114c84f1c0b4 100644 --- a/pkg/apis/abac/v1beta1/doc.go +++ b/pkg/apis/abac/v1beta1/doc.go @@ -18,6 +18,7 @@ limitations under the License. // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/abac // +k8s:openapi-gen=true // +k8s:defaulter-gen=TypeMeta +// +k8s:openapi-model-package=pkg.apis.abac.v1beta1 // +groupName=abac.authorization.kubernetes.io diff --git a/pkg/apis/abac/v1beta1/zz_generated.model_name.go b/pkg/apis/abac/v1beta1/zz_generated.model_name.go new file mode 100644 index 0000000000000..811049c7f43ab --- /dev/null +++ b/pkg/apis/abac/v1beta1/zz_generated.model_name.go @@ -0,0 +1,32 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by openapi-gen. DO NOT EDIT. + +package v1beta1 + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Policy) OpenAPIModelName() string { + return "pkg.apis.abac.v1beta1.Policy" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in PolicySpec) OpenAPIModelName() string { + return "pkg.apis.abac.v1beta1.PolicySpec" +} diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index 1665d38a26ff3..dad928096d8ce 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -1033,16 +1033,18 @@ type MutatingWebhook struct { MatchConditions []MatchCondition } -// ReinvocationPolicyType specifies what type of policy the admission hook uses. +// ReinvocationPolicyType specifies what type of policy is used when other admission plugins also perform +// modifications. +// +enum type ReinvocationPolicyType string var ( - // NeverReinvocationPolicy indicates that the webhook must not be called more than once in a + // NeverReinvocationPolicy indicates that the mutation must not be called more than once in a // single admission evaluation. NeverReinvocationPolicy ReinvocationPolicyType = "Never" - // IfNeededReinvocationPolicy indicates that the webhook may be called at least one + // IfNeededReinvocationPolicy indicates that the mutation may be called at least one // additional time as part of the admission evaluation if the object being admitted is - // modified by other admission plugins after the initial webhook call. + // modified by other admission plugins after the initial mutation call. IfNeededReinvocationPolicy ReinvocationPolicyType = "IfNeeded" ) diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 7a225b4e31893..815f51a497aea 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -38,8 +38,6 @@ import ( "k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions" "k8s.io/apiserver/pkg/cel" "k8s.io/apiserver/pkg/cel/environment" - "k8s.io/apiserver/pkg/features" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/apiserver/pkg/util/webhook" "k8s.io/client-go/util/jsonpath" @@ -226,7 +224,6 @@ func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW requireRecognizedAdmissionReviewVersion: true, requireUniqueWebhookNames: true, allowInvalidLabelValueInSelector: false, - strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks), }) } @@ -256,7 +253,6 @@ func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho requireRecognizedAdmissionReviewVersion: true, requireUniqueWebhookNames: true, allowInvalidLabelValueInSelector: false, - strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks), }) } @@ -268,7 +264,6 @@ type validationOptions struct { requireUniqueWebhookNames bool allowInvalidLabelValueInSelector bool preexistingExpressions preexistingExpressions - strictCostEnforcement bool } type preexistingExpressions struct { @@ -739,7 +734,6 @@ func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistrat requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks), allowInvalidLabelValueInSelector: validatingWebhookHasInvalidLabelValueInSelector(oldC.Webhooks), preexistingExpressions: findValidatingPreexistingExpressions(oldC), - strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks), }) } @@ -753,7 +747,6 @@ func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistratio requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks), allowInvalidLabelValueInSelector: mutatingWebhookHasInvalidLabelValueInSelector(oldC.Webhooks), preexistingExpressions: findMutatingPreexistingExpressions(oldC), - strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForWebhooks), }) } @@ -767,7 +760,7 @@ const ( // ValidateValidatingAdmissionPolicy validates a ValidatingAdmissionPolicy before creation. func ValidateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy) field.ErrorList { - return validateValidatingAdmissionPolicy(p, validationOptions{ignoreMatchConditions: false, strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForVAP)}) + return validateValidatingAdmissionPolicy(p, validationOptions{ignoreMatchConditions: false}) } func validateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmissionPolicy, opts validationOptions) field.ErrorList { @@ -782,7 +775,7 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi getCompiler := func() plugincel.Compiler { if compiler == nil { needsComposition := len(spec.Variables) > 0 - compiler = createCompiler(needsComposition, opts.strictCostEnforcement) + compiler = createCompiler(needsComposition) } return compiler } @@ -1027,7 +1020,6 @@ func validateVariable(compiler plugincel.Compiler, v *admissionregistration.Vari result := compiler.CompileAndStoreVariable(variable, plugincel.OptionalVariableDeclarations{ HasParams: paramKind != nil, HasAuthorizer: true, - StrictCost: opts.strictCostEnforcement, }, envType) if result.Error != nil { allErrors = append(allErrors, convertCELErrorToValidationError(fldPath.Child("expression"), variable, result.Error)) @@ -1102,7 +1094,6 @@ func validateValidationExpression(compiler plugincel.Compiler, expression string }, plugincel.OptionalVariableDeclarations{ HasParams: hasParams, HasAuthorizer: true, - StrictCost: opts.strictCostEnforcement, }, envType, fldPath) } @@ -1112,17 +1103,12 @@ func validateMatchConditionsExpression(expression string, opts validationOptions envType = environment.StoredExpressions } var compiler plugincel.Compiler - if opts.strictCostEnforcement { - compiler = getStrictStatelessCELCompiler() - } else { - compiler = getNonStrictStatelessCELCompiler() - } + compiler = getStrictStatelessCELCompiler() return validateCELCondition(compiler, &matchconditions.MatchCondition{ Expression: expression, }, plugincel.OptionalVariableDeclarations{ HasParams: opts.allowParamsInMatchConditions, HasAuthorizer: true, - StrictCost: opts.strictCostEnforcement, }, envType, fldPath) } @@ -1136,7 +1122,6 @@ func validateMessageExpression(compiler plugincel.Compiler, expression string, o }, plugincel.OptionalVariableDeclarations{ HasParams: opts.allowParamsInMatchConditions, HasAuthorizer: false, - StrictCost: opts.strictCostEnforcement, }, envType, fldPath) } @@ -1161,7 +1146,7 @@ func validateAuditAnnotation(compiler plugincel.Compiler, meta metav1.ObjectMeta } result := compiler.CompileCELExpression(&validatingadmissionpolicy.AuditAnnotationCondition{ ValueExpression: trimmedValueExpression, - }, plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true, StrictCost: opts.strictCostEnforcement}, envType) + }, plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true}, envType) if result.Error != nil { switch result.Error.Type { case cel.ErrorTypeRequired: @@ -1255,7 +1240,6 @@ func ValidateValidatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.V return validateValidatingAdmissionPolicy(newC, validationOptions{ ignoreMatchConditions: ignoreValidatingAdmissionPolicyMatchConditions(newC, oldC), preexistingExpressions: findValidatingPolicyPreexistingExpressions(oldC), - strictCostEnforcement: utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForVAP), }) } @@ -1320,38 +1304,24 @@ func validateFieldRef(fieldRef string, fldPath *field.Path) field.ErrorList { var ( lazyStrictStatelessCELCompilerInit sync.Once lazyStrictStatelessCELCompiler plugincel.Compiler - - lazyNonStrictStatelessCELCompilerInit sync.Once - lazyNonStrictStatelessCELCompiler plugincel.Compiler ) func getStrictStatelessCELCompiler() plugincel.Compiler { lazyStrictStatelessCELCompilerInit.Do(func() { - lazyStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true)) + lazyStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())) }) return lazyStrictStatelessCELCompiler } -func getNonStrictStatelessCELCompiler() plugincel.Compiler { - lazyNonStrictStatelessCELCompilerInit.Do(func() { - lazyNonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false)) - }) - return lazyNonStrictStatelessCELCompiler -} - -func createCompiler(allowComposition, strictCost bool) plugincel.Compiler { +func createCompiler(allowComposition bool) plugincel.Compiler { if !allowComposition { - if strictCost { - return getStrictStatelessCELCompiler() - } else { - return getNonStrictStatelessCELCompiler() - } + return getStrictStatelessCELCompiler() } - compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost)) + compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())) if err != nil { // should never happen, but cannot panic either. utilruntime.HandleError(err) - return plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost)) + return plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())) } return compiler } @@ -1380,7 +1350,6 @@ func ValidateMutatingAdmissionPolicyUpdate(newC, oldC *admissionregistration.Mut return validateMutatingAdmissionPolicy(newC, validationOptions{ ignoreMatchConditions: ignoreMutatingAdmissionPolicyMatchConditions(newC, oldC), preexistingExpressions: findMutatingPolicyPreexistingExpressions(oldC), - strictCostEnforcement: true, }) } @@ -1391,7 +1360,7 @@ func ValidateMutatingAdmissionPolicyBindingUpdate(newC, oldC *admissionregistrat // ValidateMutatingAdmissionPolicy validates a MutatingAdmissionPolicy before creation. func ValidateMutatingAdmissionPolicy(p *admissionregistration.MutatingAdmissionPolicy) field.ErrorList { - return validateMutatingAdmissionPolicy(p, validationOptions{ignoreMatchConditions: false, strictCostEnforcement: true}) + return validateMutatingAdmissionPolicy(p, validationOptions{ignoreMatchConditions: false}) } func validateMutatingAdmissionPolicy(p *admissionregistration.MutatingAdmissionPolicy, opts validationOptions) field.ErrorList { @@ -1403,7 +1372,7 @@ func validateMutatingAdmissionPolicy(p *admissionregistration.MutatingAdmissionP func validateMutatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissionregistration.MutatingAdmissionPolicySpec, opts validationOptions, fldPath *field.Path) field.ErrorList { var allErrors field.ErrorList - compiler := createCompiler(true, true) + compiler := createCompiler(true) if spec.FailurePolicy == nil { allErrors = append(allErrors, field.Required(fldPath.Child("failurePolicy"), "")) @@ -1496,7 +1465,7 @@ func validateApplyConfiguration(compiler plugincel.Compiler, applyConfig *admiss accessor := &patch.ApplyConfigurationCondition{ Expression: trimmedExpression, } - opts := plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true, StrictCost: true, HasPatchTypes: true} + opts := plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true, HasPatchTypes: true} result := compiler.CompileCELExpression(accessor, opts, envType) if result.Error != nil { @@ -1519,7 +1488,7 @@ func validateJSONPatch(compiler plugincel.Compiler, jsonPatch *admissionregistra accessor := &patch.JSONPatchCondition{ Expression: trimmedExpression, } - opts := plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true, StrictCost: true, HasPatchTypes: true} + opts := plugincel.OptionalVariableDeclarations{HasParams: paramKind != nil, HasAuthorizer: true, HasPatchTypes: true} result := compiler.CompileCELExpression(accessor, opts, envType) if result.Error != nil { diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go index ee4fbbc90660d..2b1211b8877af 100644 --- a/pkg/apis/admissionregistration/validation/validation_test.go +++ b/pkg/apis/admissionregistration/validation/validation_test.go @@ -29,8 +29,6 @@ import ( plugincel "k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/cel/environment" "k8s.io/apiserver/pkg/cel/library" - "k8s.io/apiserver/pkg/features" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/utils/ptr" ) @@ -3409,9 +3407,8 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) { }, // TODO: CustomAuditAnnotations: string valueExpression with {oldObject} is allowed } - strictCost := utilfeature.DefaultFeatureGate.Enabled(features.StrictCostEnforcementForVAP) // Include the test library, which includes the test() function in the storage environment during test - base := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost) + base := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()) extended, err := base.Extend(environment.VersionedOptions{ IntroducedVersion: version.MustParseGeneric("1.999"), EnvOptions: []cel.EnvOption{library.Test()}, @@ -3419,19 +3416,11 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) { if err != nil { t.Fatal(err) } - if strictCost { - originalCompiler := getStrictStatelessCELCompiler() - lazyStrictStatelessCELCompiler = plugincel.NewCompiler(extended) - defer func() { - lazyStrictStatelessCELCompiler = originalCompiler - }() - } else { - originalCompiler := getNonStrictStatelessCELCompiler() - lazyNonStrictStatelessCELCompiler = plugincel.NewCompiler(extended) - defer func() { - lazyNonStrictStatelessCELCompiler = originalCompiler - }() - } + originalCompiler := getStrictStatelessCELCompiler() + lazyStrictStatelessCELCompiler = plugincel.NewCompiler(extended) + defer func() { + lazyStrictStatelessCELCompiler = originalCompiler + }() for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/pkg/apis/apps/fuzzer/fuzzer.go b/pkg/apis/apps/fuzzer/fuzzer.go index 1e5aa0a344ba1..70c058f3f4021 100644 --- a/pkg/apis/apps/fuzzer/fuzzer.go +++ b/pkg/apis/apps/fuzzer/fuzzer.go @@ -26,6 +26,7 @@ import ( runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/kubernetes/pkg/apis/apps" + "k8s.io/utils/ptr" ) // Funcs returns the fuzzer functions for the apps api group. @@ -71,6 +72,9 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { if len(s.Labels) == 0 { s.Labels = s.Spec.Template.Labels } + if s.Spec.UpdateStrategy.RollingUpdate != nil && s.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable == nil { + s.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = ptr.To(intstr.FromInt32(1)) + } }, func(j *apps.Deployment, c randfill.Continue) { c.FillNoCustom(j) diff --git a/pkg/apis/apps/types.go b/pkg/apis/apps/types.go index 7e4bf6d49bd63..b8d8a1e8648fc 100644 --- a/pkg/apis/apps/types.go +++ b/pkg/apis/apps/types.go @@ -101,10 +101,12 @@ type RollingUpdateStatefulSetStrategy struct { // The maximum number of pods that can be unavailable during the update. // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). // Absolute number is calculated from percentage by rounding up. This can not be 0. - // Defaults to 1. This field is alpha-level and is only honored by servers that enable the - // MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to + // Defaults to 1. This field is beta-level and is enabled by default. The field applies to all pods in the range 0 to // Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it // will be counted towards MaxUnavailable. + // This setting might not be effective for the OrderedReady podManagementPolicy. That policy ensures pods are created and become ready one at a time. + // + // +featureGate=MaxUnavailableStatefulSet // +optional MaxUnavailable *intstr.IntOrString } @@ -229,9 +231,12 @@ type StatefulSetSpec struct { // +optional MinReadySeconds int32 - // PersistentVolumeClaimRetentionPolicy describes the policy used for PVCs created from - // the StatefulSet VolumeClaimTemplates. This requires the - // StatefulSetAutoDeletePVC feature gate to be enabled, which is beta and default on from 1.27. + // persistentVolumeClaimRetentionPolicy describes the lifecycle of persistent + // volume claims created from volumeClaimTemplates. By default, all persistent + // volume claims are created as needed and retained until manually deleted. This + // policy allows the lifecycle to be altered, for example by deleting persistent + // volume claims when their stateful set is deleted, or when their pod is scaled + // down. // +optional PersistentVolumeClaimRetentionPolicy *StatefulSetPersistentVolumeClaimRetentionPolicy @@ -533,7 +538,7 @@ type DeploymentStatus struct { // Total number of terminating pods targeted by this deployment. Terminating pods have a non-null // .metadata.deletionTimestamp and have not yet reached the Failed or Succeeded .status.phase. // - // This is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field. + // This is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default). // +optional TerminatingReplicas *int32 @@ -892,7 +897,7 @@ type ReplicaSetStatus struct { // The number of terminating pods for this replica set. Terminating pods have a non-null .metadata.deletionTimestamp // and have not yet reached the Failed or Succeeded .status.phase. // - // This is an alpha field. Enable DeploymentReplicaSetTerminatingReplicas to be able to use this field. + // This is a beta field and requires enabling DeploymentReplicaSetTerminatingReplicas feature (enabled by default). // +optional TerminatingReplicas *int32 diff --git a/pkg/apis/apps/v1/defaults.go b/pkg/apis/apps/v1/defaults.go index 2179d0238443b..b52fa0a28f636 100644 --- a/pkg/apis/apps/v1/defaults.go +++ b/pkg/apis/apps/v1/defaults.go @@ -124,16 +124,14 @@ func SetDefaults_StatefulSet(obj *appsv1.StatefulSet) { } } - if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) { - if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { - obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{} - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1.RetainPersistentVolumeClaimRetentionPolicyType - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1.RetainPersistentVolumeClaimRetentionPolicyType - } + if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { + obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{} + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1.RetainPersistentVolumeClaimRetentionPolicyType + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1.RetainPersistentVolumeClaimRetentionPolicyType } if obj.Spec.Replicas == nil { diff --git a/pkg/apis/apps/v1/defaults_test.go b/pkg/apis/apps/v1/defaults_test.go index 1b5c6cdc27e10..73935213e6141 100644 --- a/pkg/apis/apps/v1/defaults_test.go +++ b/pkg/apis/apps/v1/defaults_test.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/version" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -201,7 +200,6 @@ func TestSetDefaultStatefulSet(t *testing.T) { original *appsv1.StatefulSet expected *appsv1.StatefulSet enableMaxUnavailablePolicy bool - disablePVCDeletionPolicy bool }{ { name: "labels and default update strategy", @@ -594,76 +592,11 @@ func TestSetDefaultStatefulSet(t *testing.T) { }, enableMaxUnavailablePolicy: true, }, - { - name: "PVCDeletionPolicy disabled", - original: &appsv1.StatefulSet{ - Spec: appsv1.StatefulSetSpec{ - Template: defaultTemplate, - }, - }, - expected: &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PodManagementPolicy: appsv1.OrderedReadyPodManagement, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ - Partition: &defaultPartition, - }, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, - { - name: "PVCDeletionPolicy disabled, scaledown set", - original: &appsv1.StatefulSet{ - Spec: appsv1.StatefulSetSpec{ - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, - }, - }, - }, - expected: &appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, - }, - PodManagementPolicy: appsv1.OrderedReadyPodManagement, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ - Partition: &defaultPartition, - }, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - if test.disablePVCDeletionPolicy { - // TODO: this will be removed in 1.35 - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, false) - } featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy) obj2 := roundTrip(t, runtime.Object(test.original)) diff --git a/pkg/apis/apps/v1beta1/defaults.go b/pkg/apis/apps/v1beta1/defaults.go index 0c5e18a73a07b..46db30b6a3d7d 100644 --- a/pkg/apis/apps/v1beta1/defaults.go +++ b/pkg/apis/apps/v1beta1/defaults.go @@ -50,16 +50,14 @@ func SetDefaults_StatefulSet(obj *appsv1beta1.StatefulSet) { } } - if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) { - if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { - obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1beta1.StatefulSetPersistentVolumeClaimRetentionPolicy{} - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1beta1.RetainPersistentVolumeClaimRetentionPolicyType - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1beta1.RetainPersistentVolumeClaimRetentionPolicyType - } + if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { + obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1beta1.StatefulSetPersistentVolumeClaimRetentionPolicy{} + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1beta1.RetainPersistentVolumeClaimRetentionPolicyType + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1beta1.RetainPersistentVolumeClaimRetentionPolicyType } if obj.Spec.Replicas == nil { diff --git a/pkg/apis/apps/v1beta1/defaults_test.go b/pkg/apis/apps/v1beta1/defaults_test.go index d9019f8adcc8f..245d81aa2a827 100644 --- a/pkg/apis/apps/v1beta1/defaults_test.go +++ b/pkg/apis/apps/v1beta1/defaults_test.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/version" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -226,7 +225,6 @@ func TestSetDefaultStatefulSet(t *testing.T) { original *appsv1beta1.StatefulSet expected *appsv1beta1.StatefulSet enableMaxUnavailablePolicy bool - disablePVCDeletionPolicy bool }{ { name: "labels and default update strategy", @@ -491,80 +489,11 @@ func TestSetDefaultStatefulSet(t *testing.T) { }, enableMaxUnavailablePolicy: true, }, - { - name: "PVCDeletionPolicy disabled", - original: &appsv1beta1.StatefulSet{ - Spec: appsv1beta1.StatefulSetSpec{ - Template: defaultTemplate, - }, - }, - expected: &appsv1beta1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1beta1.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PodManagementPolicy: appsv1beta1.OrderedReadyPodManagement, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - UpdateStrategy: appsv1beta1.StatefulSetUpdateStrategy{ - Type: appsv1beta1.OnDeleteStatefulSetStrategyType, - RollingUpdate: nil, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, - { - name: "PVCDeletionPolicy disabled, scaledown set", - original: &appsv1beta1.StatefulSet{ - Spec: appsv1beta1.StatefulSetSpec{ - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1beta1.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1beta1.DeletePersistentVolumeClaimRetentionPolicyType, - }, - }, - }, - expected: &appsv1beta1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1beta1.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1beta1.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1beta1.DeletePersistentVolumeClaimRetentionPolicyType, - }, - PodManagementPolicy: appsv1beta1.OrderedReadyPodManagement, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"foo": "bar"}, - MatchExpressions: []metav1.LabelSelectorRequirement{}, - }, - UpdateStrategy: appsv1beta1.StatefulSetUpdateStrategy{ - Type: appsv1beta1.OnDeleteStatefulSetStrategyType, - RollingUpdate: nil, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - if test.disablePVCDeletionPolicy { - // TODO: this will be removed in 1.35 - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, false) - } featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy) obj2 := roundTrip(t, runtime.Object(test.original)) got, ok := obj2.(*appsv1beta1.StatefulSet) diff --git a/pkg/apis/apps/v1beta1/zz_generated.validations.go b/pkg/apis/apps/v1beta1/zz_generated.validations.go index d3e13a4f9d9e5..cc6f631c8e2a8 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta1/zz_generated.validations.go @@ -38,6 +38,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type Scale scheme.AddValidationFunc((*appsv1beta1.Scale)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/scale": @@ -48,32 +49,43 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_Scale validates an instance of Scale according +// to declarative validation rules in the API schema. func Validate_Scale(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *appsv1beta1.Scale) (errs field.ErrorList) { // field appsv1beta1.Scale.TypeMeta has no validation // field appsv1beta1.Scale.ObjectMeta has no validation // field appsv1beta1.Scale.Spec errs = append(errs, - func(fldPath *field.Path, obj, oldObj *appsv1beta1.ScaleSpec) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *appsv1beta1.ScaleSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function errs = append(errs, Validate_ScaleSpec(ctx, op, fldPath, obj, oldObj)...) return - }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *appsv1beta1.Scale) *appsv1beta1.ScaleSpec { return &oldObj.Spec }))...) + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *appsv1beta1.Scale) *appsv1beta1.ScaleSpec { return &oldObj.Spec }), oldObj != nil)...) // field appsv1beta1.Scale.Status has no validation return errs } +// Validate_ScaleSpec validates an instance of ScaleSpec according +// to declarative validation rules in the API schema. func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *appsv1beta1.ScaleSpec) (errs field.ErrorList) { // field appsv1beta1.ScaleSpec.Replicas errs = append(errs, - func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *int32, oldValueCorrelated bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation - if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { - return nil // no changes + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil } + // call field-attached validations errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return - }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) + }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta1.ScaleSpec) *int32 { return &oldObj.Replicas }), oldObj != nil)...) return errs } diff --git a/pkg/apis/apps/v1beta2/defaults.go b/pkg/apis/apps/v1beta2/defaults.go index 1ed7190fb8c13..d1598d9f7aeba 100644 --- a/pkg/apis/apps/v1beta2/defaults.go +++ b/pkg/apis/apps/v1beta2/defaults.go @@ -81,16 +81,14 @@ func SetDefaults_StatefulSet(obj *appsv1beta2.StatefulSet) { } } - if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) { - if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { - obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1beta2.StatefulSetPersistentVolumeClaimRetentionPolicy{} - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1beta2.RetainPersistentVolumeClaimRetentionPolicyType - } - if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { - obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1beta2.RetainPersistentVolumeClaimRetentionPolicyType - } + if obj.Spec.PersistentVolumeClaimRetentionPolicy == nil { + obj.Spec.PersistentVolumeClaimRetentionPolicy = &appsv1beta2.StatefulSetPersistentVolumeClaimRetentionPolicy{} + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = appsv1beta2.RetainPersistentVolumeClaimRetentionPolicyType + } + if len(obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled) == 0 { + obj.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled = appsv1beta2.RetainPersistentVolumeClaimRetentionPolicyType } if obj.Spec.Replicas == nil { diff --git a/pkg/apis/apps/v1beta2/defaults_test.go b/pkg/apis/apps/v1beta2/defaults_test.go index 43ac5c518bc33..6cfc54c8e9ba5 100644 --- a/pkg/apis/apps/v1beta2/defaults_test.go +++ b/pkg/apis/apps/v1beta2/defaults_test.go @@ -27,7 +27,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/version" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -201,7 +200,6 @@ func TestSetDefaultStatefulSet(t *testing.T) { original *appsv1beta2.StatefulSet expected *appsv1beta2.StatefulSet enableMaxUnavailablePolicy bool - disablePVCDeletionPolicy bool }{ { name: "labels and default update strategy", @@ -466,76 +464,11 @@ func TestSetDefaultStatefulSet(t *testing.T) { }, enableMaxUnavailablePolicy: true, }, - { - name: "PVCDeletionPolicy disabled", - original: &appsv1beta2.StatefulSet{ - Spec: appsv1beta2.StatefulSetSpec{ - Template: defaultTemplate, - }, - }, - expected: &appsv1beta2.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1beta2.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement, - UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{ - Type: appsv1beta2.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &appsv1beta2.RollingUpdateStatefulSetStrategy{ - Partition: &defaultPartition, - }, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, - { - name: "PVCDeletionPolicy disabled, scaledown set", - original: &appsv1beta2.StatefulSet{ - Spec: appsv1beta2.StatefulSetSpec{ - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1beta2.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1beta2.DeletePersistentVolumeClaimRetentionPolicyType, - }, - }, - }, - expected: &appsv1beta2.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Labels: defaultLabels, - }, - Spec: appsv1beta2.StatefulSetSpec{ - Replicas: &defaultReplicas, - MinReadySeconds: int32(0), - Template: defaultTemplate, - PersistentVolumeClaimRetentionPolicy: &appsv1beta2.StatefulSetPersistentVolumeClaimRetentionPolicy{ - WhenScaled: appsv1beta2.DeletePersistentVolumeClaimRetentionPolicyType, - }, - PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement, - UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{ - Type: appsv1beta2.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &appsv1beta2.RollingUpdateStatefulSetStrategy{ - Partition: &defaultPartition, - }, - }, - RevisionHistoryLimit: ptr.To[int32](10), - }, - }, - disablePVCDeletionPolicy: true, - }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - if test.disablePVCDeletionPolicy { - // TODO: this will be removed in 1.35 - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, false) - } featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy) obj2 := roundTrip(t, runtime.Object(test.original)) got, ok := obj2.(*appsv1beta2.StatefulSet) diff --git a/pkg/apis/apps/v1beta2/zz_generated.validations.go b/pkg/apis/apps/v1beta2/zz_generated.validations.go index 3c16d10b67441..0499add423c40 100644 --- a/pkg/apis/apps/v1beta2/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta2/zz_generated.validations.go @@ -38,6 +38,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type Scale scheme.AddValidationFunc((*appsv1beta2.Scale)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/scale": @@ -48,32 +49,43 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_Scale validates an instance of Scale according +// to declarative validation rules in the API schema. func Validate_Scale(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *appsv1beta2.Scale) (errs field.ErrorList) { // field appsv1beta2.Scale.TypeMeta has no validation // field appsv1beta2.Scale.ObjectMeta has no validation // field appsv1beta2.Scale.Spec errs = append(errs, - func(fldPath *field.Path, obj, oldObj *appsv1beta2.ScaleSpec) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *appsv1beta2.ScaleSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function errs = append(errs, Validate_ScaleSpec(ctx, op, fldPath, obj, oldObj)...) return - }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *appsv1beta2.Scale) *appsv1beta2.ScaleSpec { return &oldObj.Spec }))...) + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *appsv1beta2.Scale) *appsv1beta2.ScaleSpec { return &oldObj.Spec }), oldObj != nil)...) // field appsv1beta2.Scale.Status has no validation return errs } +// Validate_ScaleSpec validates an instance of ScaleSpec according +// to declarative validation rules in the API schema. func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *appsv1beta2.ScaleSpec) (errs field.ErrorList) { // field appsv1beta2.ScaleSpec.Replicas errs = append(errs, - func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *int32, oldValueCorrelated bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation - if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { - return nil // no changes + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil } + // call field-attached validations errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return - }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta2.ScaleSpec) *int32 { return &oldObj.Replicas }))...) + }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta2.ScaleSpec) *int32 { return &oldObj.Replicas }), oldObj != nil)...) return errs } diff --git a/pkg/apis/autoscaling/types.go b/pkg/apis/autoscaling/types.go index db9ac44083b04..b8802a992b2af 100644 --- a/pkg/apis/autoscaling/types.go +++ b/pkg/apis/autoscaling/types.go @@ -149,7 +149,7 @@ const ( // // The tolerance is applied to the metric values and prevents scaling too // eagerly for small metric variations. (Note that setting a tolerance requires -// enabling the alpha HPAConfigurableTolerance feature gate.) +// the beta HPAConfigurableTolerance feature gate to be enabled.) type HPAScalingRules struct { // StabilizationWindowSeconds is the number of seconds for which past recommendations should be // considered while scaling up or scaling down. @@ -178,8 +178,8 @@ type HPAScalingRules struct { // and scale-down and scale-up tolerances of 5% and 1% respectively, scaling will be // triggered when the actual consumption falls below 95Mi or exceeds 101Mi. // - // This is an alpha field and requires enabling the HPAConfigurableTolerance - // feature gate. + // This is an beta field and requires the HPAConfigurableTolerance feature + // gate to be enabled. // // +featureGate=HPAConfigurableTolerance // +optional diff --git a/pkg/apis/autoscaling/v1/zz_generated.validations.go b/pkg/apis/autoscaling/v1/zz_generated.validations.go index 1c71f7f80e537..cfb0a3cd4b3c3 100644 --- a/pkg/apis/autoscaling/v1/zz_generated.validations.go +++ b/pkg/apis/autoscaling/v1/zz_generated.validations.go @@ -38,6 +38,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type Scale scheme.AddValidationFunc((*autoscalingv1.Scale)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/scale": @@ -48,32 +49,43 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_Scale validates an instance of Scale according +// to declarative validation rules in the API schema. func Validate_Scale(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *autoscalingv1.Scale) (errs field.ErrorList) { // field autoscalingv1.Scale.TypeMeta has no validation // field autoscalingv1.Scale.ObjectMeta has no validation // field autoscalingv1.Scale.Spec errs = append(errs, - func(fldPath *field.Path, obj, oldObj *autoscalingv1.ScaleSpec) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *autoscalingv1.ScaleSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function errs = append(errs, Validate_ScaleSpec(ctx, op, fldPath, obj, oldObj)...) return - }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *autoscalingv1.Scale) *autoscalingv1.ScaleSpec { return &oldObj.Spec }))...) + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *autoscalingv1.Scale) *autoscalingv1.ScaleSpec { return &oldObj.Spec }), oldObj != nil)...) // field autoscalingv1.Scale.Status has no validation return errs } +// Validate_ScaleSpec validates an instance of ScaleSpec according +// to declarative validation rules in the API schema. func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *autoscalingv1.ScaleSpec) (errs field.ErrorList) { // field autoscalingv1.ScaleSpec.Replicas errs = append(errs, - func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *int32, oldValueCorrelated bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation - if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { - return nil // no changes + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil } + // call field-attached validations errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return - }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *autoscalingv1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) + }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *autoscalingv1.ScaleSpec) *int32 { return &oldObj.Replicas }), oldObj != nil)...) return errs } diff --git a/pkg/apis/autoscaling/validation/declarative_validation_test.go b/pkg/apis/autoscaling/validation/declarative_validation_test.go index 1e46787be2512..514bb9c70397b 100644 --- a/pkg/apis/autoscaling/validation/declarative_validation_test.go +++ b/pkg/apis/autoscaling/validation/declarative_validation_test.go @@ -17,27 +17,27 @@ limitations under the License. package validation import ( - "fmt" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/rest" - utilfeature "k8s.io/apiserver/pkg/util/feature" - featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/apis/autoscaling" - "k8s.io/kubernetes/pkg/features" ) -func TestValidateScaleForDeclarative(t *testing.T) { +// TestScaleDeclarativeValidation verifies that the validation rules that are applied uniformly to the Scale API. +func TestScaleDeclarativeValidation(t *testing.T) { + apiGroup := "autoscaling" + apiVersion := "v1" ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{ - APIGroup: "autoscaling", - APIVersion: "v1", + APIGroup: apiGroup, + APIVersion: apiVersion, Subresource: "scale", }) + testCases := map[string]struct { input autoscaling.Scale expectedErrs field.ErrorList @@ -58,37 +58,15 @@ func TestValidateScaleForDeclarative(t *testing.T) { } for k, tc := range testCases { t.Run(k, func(t *testing.T) { - var declarativeTakeoverErrs field.ErrorList - var imperativeErrs field.ErrorList - for _, gateVal := range []bool{true, false} { - t.Run(fmt.Sprintf("gates=%v", gateVal), func(t *testing.T) { - // We only need to test both gate enabled and disabled together, because - // 1) the DeclarativeValidationTakeover won't take effect if DeclarativeValidation is disabled. - // 2) the validation output, when only DeclarativeValidation is enabled, is the same as when both gates are disabled. - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidation, gateVal) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidationTakeover, gateVal) - - errs := rest.ValidateDeclaratively(ctx, legacyscheme.Scheme, &tc.input) - if gateVal { - declarativeTakeoverErrs = errs - } else { - imperativeErrs = errs - } - // The errOutputMatcher is used to verify the output matches the expected errors in test cases. - errOutputMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() - if len(tc.expectedErrs) > 0 { - errOutputMatcher.Test(t, tc.expectedErrs, errs) - } else if len(errs) != 0 { - t.Errorf("expected no errors, but got: %v", errs) - } - }) - } - // The equivalenceMatcher is used to verify the output errors from handwritten imperative validation - // are equivalent to the output errors when DeclarativeValidationTakeover is enabled. - equivalenceMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() - equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) + // The Scale API has no handwritten validation to compare against. So we simply test that all the versions + // of the Scale subresource are correct for our test cases. + // All resources that have a scale subresource are expected to test Scale validation against any handwritten + // validation code defined on that resource. + tester := field.ErrorMatcher{}.ByType().ByField().ByOrigin() + obj, _ := legacyscheme.Scheme.ConvertToVersion(&tc.input, schema.GroupVersion{Group: apiGroup, Version: apiVersion}) + tester.Test(t, tc.expectedErrs, legacyscheme.Scheme.Validate(ctx, nil, obj, "scale")) - apitesting.VerifyVersionedValidationEquivalence(t, &tc.input, nil, "scale") + apitesting.VerifyVersionedValidationEquivalence(t, &tc.input, nil, apitesting.WithSubResources("scale")) }) } } diff --git a/pkg/apis/autoscaling/validation/validation.go b/pkg/apis/autoscaling/validation/validation.go index 2b34e46f32f76..524212f53a105 100644 --- a/pkg/apis/autoscaling/validation/validation.go +++ b/pkg/apis/autoscaling/validation/validation.go @@ -50,7 +50,7 @@ func ValidateScale(scale *autoscaling.Scale) field.ErrorList { // ValidateHorizontalPodAutoscalerName can be used to check whether the given autoscaler name is valid. // Prefix indicates this name will be used as part of generation, in which case trailing dashes are allowed. -var ValidateHorizontalPodAutoscalerName = apivalidation.ValidateReplicationControllerName +var ValidateHorizontalPodAutoscalerName = apimachineryvalidation.NameIsDNSSubdomain func validateHorizontalPodAutoscalerSpec(autoscaler autoscaling.HorizontalPodAutoscalerSpec, fldPath *field.Path, opts HorizontalPodAutoscalerSpecValidationOptions) field.ErrorList { allErrs := field.ErrorList{} diff --git a/pkg/apis/batch/types.go b/pkg/apis/batch/types.go index 7350bc7c1728d..5fb3d8f82339f 100644 --- a/pkg/apis/batch/types.go +++ b/pkg/apis/batch/types.go @@ -212,6 +212,7 @@ type PodFailurePolicyOnPodConditionsPattern struct { // Specifies the required Pod condition status. To match a pod condition // it is required that the specified status equals the pod condition status. // Defaults to True. + // +optional Status api.ConditionStatus } @@ -466,9 +467,6 @@ type JobSpec struct { // by RFC 1123. All characters trailing the first "/" must be valid HTTP Path // characters as defined by RFC 3986. The value cannot exceed 63 characters. // This field is immutable. - // - // This field is beta-level. The job controller accepts setting the field - // when the feature gate JobManagedBy is enabled (enabled by default). // +optional ManagedBy *string } diff --git a/pkg/apis/batch/validation/validation.go b/pkg/apis/batch/validation/validation.go index 17ffbbe4b4b4a..6e7e093145531 100644 --- a/pkg/apis/batch/validation/validation.go +++ b/pkg/apis/batch/validation/validation.go @@ -23,9 +23,8 @@ import ( "strings" "time" - "github.com/robfig/cron/v3" - apiequality "k8s.io/apimachinery/pkg/api/equality" + apimachineryapivalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/labels" @@ -36,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/apis/batch" api "k8s.io/kubernetes/pkg/apis/core" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" + "k8s.io/kubernetes/pkg/util/parsers" "k8s.io/utils/ptr" ) @@ -150,7 +150,7 @@ func validateGeneratedSelector(obj *batch.Job, validateBatchLabels bool) field.E // ValidateJob validates a Job and returns an ErrorList with any errors. func ValidateJob(job *batch.Job, opts JobValidationOptions) field.ErrorList { // Jobs and rcs have the same name validation - allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata")) + allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apimachineryapivalidation.NameIsDNSSubdomain, field.NewPath("metadata")) allErrs = append(allErrs, validateGeneratedSelector(job, opts.RequirePrefixedLabels)...) allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"), opts.PodValidationOptions)...) if job.Spec.CompletionMode != nil && *job.Spec.CompletionMode == batch.IndexedCompletion && job.Spec.Completions != nil && *job.Spec.Completions > 0 { @@ -660,7 +660,49 @@ func validatePodTemplateUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path, oldTemplate.Labels = template.Labels // +k8s:verify-mutation:reason=clone oldTemplate.Spec.SchedulingGates = template.Spec.SchedulingGates // +k8s:verify-mutation:reason=clone } - allErrs = append(allErrs, apivalidation.ValidateImmutableField(template, oldTemplate, fldPath.Child("template"))...) + suspended := oldSpec.Suspend != nil && *oldSpec.Suspend + if !suspended { + // if the job isn't suspended, then we cannot allow any mutation of the template + allErrs = append(allErrs, apivalidation.ValidateImmutableField(template, oldTemplate, fldPath.Child("template"))...) + } else { + if !opts.AllowMutablePodResources { + allErrs = append(allErrs, apivalidation.ValidateImmutableField(template, oldTemplate, fldPath.Child("template"))...) + } else { + // Only allow container resource updates when AllowMutablePodResources is true + allErrs = append(allErrs, validatePodResourceUpdatesOnly(template.Spec, oldTemplate.Spec, fldPath.Child("template.spec"))...) + } + } + return allErrs +} + +// validatePodResourceUpdatesOnly will validate that only container resources are updated. +func validatePodResourceUpdatesOnly(newPod api.PodSpec, oldPod api.PodSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + // Create a deep copy of the old pod spec to modify + oldPodCopy := oldPod.DeepCopy() + // Update container resources in the copy to match the new pod spec + if len(newPod.Containers) == len(oldPodCopy.Containers) { + for i := range newPod.Containers { + if oldPodCopy.Containers[i].Name == newPod.Containers[i].Name { + oldPodCopy.Containers[i].Resources = newPod.Containers[i].Resources // +k8s:verify-mutation:reason=clone + } + } + } + + // Update init container resources in the copy to match the new pod spec + if len(newPod.InitContainers) == len(oldPodCopy.InitContainers) { + for i := range newPod.InitContainers { + if oldPodCopy.InitContainers[i].Name == newPod.InitContainers[i].Name { + oldPodCopy.InitContainers[i].Resources = newPod.InitContainers[i].Resources // +k8s:verify-mutation:reason=clone + } + } + } + + // Validate that the modified copy is identical to the new pod spec + // This ensures only container resources were changed + allErrs = append(allErrs, apivalidation.ValidateImmutableField(newPod, *oldPodCopy, fldPath)...) + return allErrs } @@ -714,7 +756,7 @@ func ValidateJobStatusUpdate(job, oldJob *batch.Job, opts JobStatusValidationOpt // ValidateCronJobCreate validates a CronJob on creation and returns an ErrorList with any errors. func ValidateCronJobCreate(cronJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList { // CronJobs and rcs have the same name validation - allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata")) + allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apimachineryapivalidation.NameIsDNSSubdomain, field.NewPath("metadata")) allErrs = append(allErrs, validateCronJobSpec(&cronJob.Spec, nil, field.NewPath("spec"), opts)...) if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 { // The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when @@ -790,7 +832,8 @@ func validateConcurrencyPolicy(concurrencyPolicy *batch.ConcurrencyPolicy, fldPa func validateScheduleFormat(schedule string, allowTZInSchedule bool, timeZone *string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if _, err := cron.ParseStandard(schedule); err != nil { + + if _, err := parsers.ParseCronScheduleWithPanicRecovery(schedule); err != nil { allErrs = append(allErrs, field.Invalid(fldPath, schedule, err.Error())) } switch { @@ -1022,6 +1065,8 @@ type JobValidationOptions struct { AllowMutableSchedulingDirectives bool // Require Job to have the label on batch.kubernetes.io/job-name and batch.kubernetes.io/controller-uid RequirePrefixedLabels bool + // Allow mutable pod resources + AllowMutablePodResources bool } type JobStatusValidationOptions struct { diff --git a/pkg/apis/batch/validation/validation_test.go b/pkg/apis/batch/validation/validation_test.go index 2e30d0e1998fa..3bf0791852db1 100644 --- a/pkg/apis/batch/validation/validation_test.go +++ b/pkg/apis/batch/validation/validation_test.go @@ -18,15 +18,15 @@ package validation import ( "errors" - _ "time/tzdata" - "fmt" "strings" "testing" + _ "time/tzdata" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" @@ -2243,6 +2243,326 @@ func TestValidateJobUpdate(t *testing.T) { job.Spec.Parallelism = ptr.To[int32](3) }, }, + "allow container resource updates only": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + Suspend: ptr.To(true), + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.Containers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + api.ResourceMemory: resource.MustParse("256Mi"), + }, + Limits: api.ResourceList{ + api.ResourceCPU: resource.MustParse("500m"), + api.ResourceMemory: resource.MustParse("512Mi"), + }, + } + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + }, + "reject container resource updates only with feature gate off": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + Suspend: ptr.To(true), + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.Containers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + api.ResourceMemory: resource.MustParse("256Mi"), + }, + Limits: api.ResourceList{ + api.ResourceCPU: resource.MustParse("500m"), + api.ResourceMemory: resource.MustParse("512Mi"), + }, + } + }, + opts: JobValidationOptions{ + AllowMutablePodResources: false, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template", + }, + }, + "allow init container resource updates only": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: func() api.PodTemplateSpec { + template := getValidPodTemplateSpecForGenerated(validGeneratedSelector) + template.Spec.InitContainers = []api.Container{ + podtest.MakeContainer("init-container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + } + return template + }(), + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.InitContainers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + api.ResourceMemory: resource.MustParse("128Mi"), + }, + Limits: api.ResourceList{ + api.ResourceCPU: resource.MustParse("400m"), + api.ResourceMemory: resource.MustParse("256Mi"), + }, + } + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + }, + "allow both container and init container resource updates": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: func() api.PodTemplateSpec { + template := getValidPodTemplateSpecForGenerated(validGeneratedSelector) + template.Spec.InitContainers = []api.Container{ + podtest.MakeContainer("init-container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + } + return template + }(), + }, + }, + update: func(job *batch.Job) { + job.Spec.Template.Spec.Containers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + api.ResourceMemory: resource.MustParse("256Mi"), + }, + } + job.Spec.Template.Spec.InitContainers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("150m"), + api.ResourceMemory: resource.MustParse("128Mi"), + }, + } + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + }, + "reject container resource update with other field changes": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + }, + }, + update: func(job *batch.Job) { + // Update container resources + job.Spec.Template.Spec.Containers[0].Resources = api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("200m"), + api.ResourceMemory: resource.MustParse("256Mi"), + }, + } + // Also update another field (should cause validation failure) + job.Spec.Template.Spec.Containers[0].Image = "nginx:latest" + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, + "reject non-resource field updates when AllowMutablePodResources is true": { + old: batch.Job{ + + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + }, + }, + update: func(job *batch.Job) { + // Update only a non-resource field + job.Spec.Template.Spec.Containers[0].Image = "nginx:latest" + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, + "reject pod spec changes when container count differs": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + }, + }, + update: func(job *batch.Job) { + // Add a new container (changes container count) + job.Spec.Template.Spec.Containers = append(job.Spec.Template.Spec.Containers, podtest.MakeContainer("sidecar")) + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, + "reject pod spec changes when init container count differs": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + }, + }, + update: func(job *batch.Job) { + // Add an init container (changes init container count) + job.Spec.Template.Spec.InitContainers = []api.Container{ + podtest.MakeContainer("init-container"), + } + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, + "reject pod spec changes if update reorders init containers": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: func() api.PodTemplateSpec { + template := getValidPodTemplateSpecForGenerated(validGeneratedSelector) + template.Spec.InitContainers = []api.Container{ + podtest.MakeContainer("init-container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + podtest.MakeContainer("init-container-2", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + } + return template + }(), + }, + }, + update: func(job *batch.Job) { + // Reorder init container (changes init container count) + job.Spec.Template.Spec.InitContainers[0] = podtest.MakeContainer("init-container-2", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })) + job.Spec.Template.Spec.InitContainers[1] = podtest.MakeContainer("init-container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })) + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, + "reject pod spec changes if update reorders containers": { + old: batch.Job{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: batch.JobSpec{ + Suspend: ptr.To(true), + Selector: validGeneratedSelector, + Template: func() api.PodTemplateSpec { + template := getValidPodTemplateSpecForGenerated(validGeneratedSelector) + template.Spec.Containers = []api.Container{ + podtest.MakeContainer("container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + podtest.MakeContainer("container-2", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })), + } + return template + }(), + }, + }, + update: func(job *batch.Job) { + // Reorder init container (changes init container count) + job.Spec.Template.Spec.Containers[0] = podtest.MakeContainer("container-2", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })) + job.Spec.Template.Spec.Containers[1] = podtest.MakeContainer("container", podtest.SetContainerResources(api.ResourceRequirements{ + Requests: api.ResourceList{ + api.ResourceCPU: resource.MustParse("100m"), + api.ResourceMemory: resource.MustParse("64Mi"), + }, + })) + }, + opts: JobValidationOptions{ + AllowMutablePodResources: true, + }, + err: &field.Error{ + Type: field.ErrorTypeInvalid, + Field: "spec.template.spec", + }, + }, } ignoreValueAndDetail := cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail") for k, tc := range cases { @@ -4002,3 +4322,132 @@ func TestValidateFailedIndexesNotOverlapCompleted(t *testing.T) { }) } } + +// TestValidateScheduleFormatPanicRecovery tests that validateScheduleFormat +// properly recovers from panics in cron.ParseStandard and returns validation errors +func TestValidateScheduleFormatPanicRecovery(t *testing.T) { + testCases := []struct { + name string + schedule string + expected string + }{ + { + name: "TZ=0 without space should not panic", + schedule: "TZ=0", + expected: "invalid schedule format", + }, + { + name: "TZ= without value should not panic", + schedule: "TZ=", + expected: "invalid schedule format", + }, + { + name: "CRON_TZ= without space should not panic", + schedule: "CRON_TZ=UTC", + expected: "invalid schedule format", + }, + { + name: "malformed timezone spec should not panic", + schedule: "TZ=Invalid/Timezone", + expected: "invalid schedule format", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // This should not panic and should return a validation error + errs := validateScheduleFormat(tc.schedule, false, nil, field.NewPath("spec", "schedule")) + + if len(errs) == 0 { + t.Errorf("Expected validation error for schedule %q, but got none", tc.schedule) + return + } + + // Check that the error message contains expected text + errMsg := errs[0].Error() + if !strings.Contains(errMsg, tc.expected) { + t.Errorf("Expected error message to contain %q, but got: %s", tc.expected, errMsg) + } + + // Verify it's an Invalid field error + if errs[0].Type != field.ErrorTypeInvalid { + t.Errorf("Expected ErrorTypeInvalid, but got: %v", errs[0].Type) + } + }) + } +} + +// TestValidateScheduleFormatNormalCases tests normal validation cases +// to ensure panic recovery doesn't interfere with normal operation +func TestValidateScheduleFormatNormalCases(t *testing.T) { + testCases := []struct { + name string + schedule string + allowTZInSchedule bool + timeZone *string + expectError bool + expectedError string + }{ + { + name: "valid schedule without timezone", + schedule: "0 0 * * *", + allowTZInSchedule: false, + timeZone: nil, + expectError: false, + }, + { + name: "valid schedule with timezone field", + schedule: "0 0 * * *", + allowTZInSchedule: false, + timeZone: &timeZoneUTC, + expectError: false, + }, + { + name: "TZ in schedule when not allowed", + schedule: "TZ=UTC 0 0 * * *", + allowTZInSchedule: false, + timeZone: nil, + expectError: true, + expectedError: "cannot use TZ or CRON_TZ in schedule", + }, + { + name: "TZ in schedule when allowed", + schedule: "TZ=UTC 0 0 * * *", + allowTZInSchedule: true, + timeZone: nil, + expectError: false, + }, + { + name: "TZ in schedule with timezone field", + schedule: "TZ=UTC 0 0 * * *", + allowTZInSchedule: true, + timeZone: &timeZoneUTC, + expectError: true, + expectedError: "cannot use both timeZone field and TZ or CRON_TZ in schedule", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + errs := validateScheduleFormat(tc.schedule, tc.allowTZInSchedule, tc.timeZone, field.NewPath("spec", "schedule")) + + if tc.expectError { + if len(errs) == 0 { + t.Errorf("Expected validation error for schedule %q, but got none", tc.schedule) + return + } + + if tc.expectedError != "" { + errMsg := errs[0].Error() + if !strings.Contains(errMsg, tc.expectedError) { + t.Errorf("Expected error message to contain %q, but got: %s", tc.expectedError, errMsg) + } + } + } else { + if len(errs) > 0 { + t.Errorf("Expected no validation errors for schedule %q, but got: %v", tc.schedule, errs) + } + } + }) + } +} diff --git a/pkg/apis/certificates/types.go b/pkg/apis/certificates/types.go index 61922c20103c0..1f7e345406b64 100644 --- a/pkg/apis/certificates/types.go +++ b/pkg/apis/certificates/types.go @@ -375,6 +375,18 @@ type PodCertificateRequestSpec struct { // [ED25519 Specification](https://ed25519.cr.yp.to/) (as implemented by the // golang library crypto/ed25519.Sign). ProofOfPossession []byte + + // unverifiedUserAnnotations allow pod authors to pass additional information to + // the signer implementation. Kubernetes does not restrict or validate this + // metadata in any way. + // + // Entries are subject to the same validation as object metadata annotations, + // with the addition that all keys must be domain-prefixed. No restrictions + // are placed on values, except an overall size limitation on the entire field. + // + // Signers should document the keys and values they support. Signers should + // deny requests that contain keys they do not recognize. + UnverifiedUserAnnotations map[string]string } type PodCertificateRequestStatus struct { @@ -458,6 +470,11 @@ const ( // UnsupportedKeyType should be set on "Denied" conditions when the signer // doesn't support the key type of publicKey. PodCertificateRequestConditionUnsupportedKeyType string = "UnsupportedKeyType" + + // InvalidUnverifiedUserAnnotations should be set on "Denied" conditions when the signer + // does not recognize one of the keys passed in userConfig, or if the signer + // otherwise considers the userConfig of the request to be invalid. + PodCertificateRequestConditionInvalidUserConfig string = "InvalidUnverifiedUserAnnotations" ) const ( diff --git a/pkg/apis/certificates/v1/zz_generated.validations.go b/pkg/apis/certificates/v1/zz_generated.validations.go index 42a1716c4d9b0..0e462a1700dc0 100644 --- a/pkg/apis/certificates/v1/zz_generated.validations.go +++ b/pkg/apis/certificates/v1/zz_generated.validations.go @@ -39,6 +39,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type CertificateSigningRequest scheme.AddValidationFunc((*certificatesv1.CertificateSigningRequest)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/", "/approval", "/status": @@ -46,6 +47,7 @@ func RegisterValidations(scheme *runtime.Scheme) error { } return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} }) + // type CertificateSigningRequestList scheme.AddValidationFunc((*certificatesv1.CertificateSigningRequestList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/": @@ -56,6 +58,8 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_CertificateSigningRequest validates an instance of CertificateSigningRequest according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1.CertificateSigningRequest) (errs field.ErrorList) { // field certificatesv1.CertificateSigningRequest.TypeMeta has no validation // field certificatesv1.CertificateSigningRequest.ObjectMeta has no validation @@ -63,48 +67,66 @@ func Validate_CertificateSigningRequest(ctx context.Context, op operation.Operat // field certificatesv1.CertificateSigningRequest.Status errs = append(errs, - func(fldPath *field.Path, obj, oldObj *certificatesv1.CertificateSigningRequestStatus) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *certificatesv1.CertificateSigningRequestStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function errs = append(errs, Validate_CertificateSigningRequestStatus(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *certificatesv1.CertificateSigningRequest) *certificatesv1.CertificateSigningRequestStatus { return &oldObj.Status - }))...) + }), oldObj != nil)...) return errs } +// Validate_CertificateSigningRequestList validates an instance of CertificateSigningRequestList according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequestList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1.CertificateSigningRequestList) (errs field.ErrorList) { // field certificatesv1.CertificateSigningRequestList.TypeMeta has no validation // field certificatesv1.CertificateSigningRequestList.ListMeta has no validation // field certificatesv1.CertificateSigningRequestList.Items errs = append(errs, - func(fldPath *field.Path, obj, oldObj []certificatesv1.CertificateSigningRequest) (errs field.ErrorList) { - if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj []certificatesv1.CertificateSigningRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil } + // iterate the list and call the type's validation function errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_CertificateSigningRequest)...) return }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *certificatesv1.CertificateSigningRequestList) []certificatesv1.CertificateSigningRequest { return oldObj.Items - }))...) + }), oldObj != nil)...) return errs } -var zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1_CertificateSigningRequestStatus_Conditions_ = validate.NewUnionMembership([2]string{"Conditions[{\"type\": \"Approved\"}]", ""}, [2]string{"Conditions[{\"type\": \"Denied\"}]", ""}) +var zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1_CertificateSigningRequestStatus_conditions_ = validate.NewUnionMembership(validate.NewUnionMember("conditions[{\"type\": \"Approved\"}]"), validate.NewUnionMember("conditions[{\"type\": \"Denied\"}]")) +// Validate_CertificateSigningRequestStatus validates an instance of CertificateSigningRequestStatus according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequestStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1.CertificateSigningRequestStatus) (errs field.ErrorList) { // field certificatesv1.CertificateSigningRequestStatus.Conditions errs = append(errs, - func(fldPath *field.Path, obj, oldObj []certificatesv1.CertificateSigningRequestCondition) (errs field.ErrorList) { - if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj []certificatesv1.CertificateSigningRequestCondition, oldValueCorrelated bool) (errs field.ErrorList) { + // Uniqueness validation is implemented via custom, handwritten validation + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil } + // call field-attached validations + earlyReturn := false if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { return // do not proceed } - errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1_CertificateSigningRequestStatus_Conditions_, func(list []certificatesv1.CertificateSigningRequestCondition) bool { + errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1_CertificateSigningRequestStatus_conditions_, func(list []certificatesv1.CertificateSigningRequestCondition) bool { for i := range list { if list[i].Type == "Approved" { return true @@ -122,7 +144,7 @@ func Validate_CertificateSigningRequestStatus(ctx context.Context, op operation. return }(fldPath.Child("conditions"), obj.Conditions, safe.Field(oldObj, func(oldObj *certificatesv1.CertificateSigningRequestStatus) []certificatesv1.CertificateSigningRequestCondition { return oldObj.Conditions - }))...) + }), oldObj != nil)...) // field certificatesv1.CertificateSigningRequestStatus.Certificate has no validation return errs diff --git a/pkg/apis/certificates/v1alpha1/zz_generated.conversion.go b/pkg/apis/certificates/v1alpha1/zz_generated.conversion.go index da331bf930a33..f7f490e725983 100644 --- a/pkg/apis/certificates/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/certificates/v1alpha1/zz_generated.conversion.go @@ -25,10 +25,8 @@ import ( unsafe "unsafe" certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" - types "k8s.io/apimachinery/pkg/types" certificates "k8s.io/kubernetes/pkg/apis/certificates" ) @@ -69,46 +67,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*certificatesv1alpha1.PodCertificateRequest)(nil), (*certificates.PodCertificateRequest)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_PodCertificateRequest_To_certificates_PodCertificateRequest(a.(*certificatesv1alpha1.PodCertificateRequest), b.(*certificates.PodCertificateRequest), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequest)(nil), (*certificatesv1alpha1.PodCertificateRequest)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_certificates_PodCertificateRequest_To_v1alpha1_PodCertificateRequest(a.(*certificates.PodCertificateRequest), b.(*certificatesv1alpha1.PodCertificateRequest), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificatesv1alpha1.PodCertificateRequestList)(nil), (*certificates.PodCertificateRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(a.(*certificatesv1alpha1.PodCertificateRequestList), b.(*certificates.PodCertificateRequestList), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestList)(nil), (*certificatesv1alpha1.PodCertificateRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_certificates_PodCertificateRequestList_To_v1alpha1_PodCertificateRequestList(a.(*certificates.PodCertificateRequestList), b.(*certificatesv1alpha1.PodCertificateRequestList), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificatesv1alpha1.PodCertificateRequestSpec)(nil), (*certificates.PodCertificateRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(a.(*certificatesv1alpha1.PodCertificateRequestSpec), b.(*certificates.PodCertificateRequestSpec), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestSpec)(nil), (*certificatesv1alpha1.PodCertificateRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec(a.(*certificates.PodCertificateRequestSpec), b.(*certificatesv1alpha1.PodCertificateRequestSpec), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificatesv1alpha1.PodCertificateRequestStatus)(nil), (*certificates.PodCertificateRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(a.(*certificatesv1alpha1.PodCertificateRequestStatus), b.(*certificates.PodCertificateRequestStatus), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestStatus)(nil), (*certificatesv1alpha1.PodCertificateRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus(a.(*certificates.PodCertificateRequestStatus), b.(*certificatesv1alpha1.PodCertificateRequestStatus), scope) - }); err != nil { - return err - } return nil } @@ -181,123 +139,3 @@ func autoConvert_certificates_ClusterTrustBundleSpec_To_v1alpha1_ClusterTrustBun func Convert_certificates_ClusterTrustBundleSpec_To_v1alpha1_ClusterTrustBundleSpec(in *certificates.ClusterTrustBundleSpec, out *certificatesv1alpha1.ClusterTrustBundleSpec, s conversion.Scope) error { return autoConvert_certificates_ClusterTrustBundleSpec_To_v1alpha1_ClusterTrustBundleSpec(in, out, s) } - -func autoConvert_v1alpha1_PodCertificateRequest_To_certificates_PodCertificateRequest(in *certificatesv1alpha1.PodCertificateRequest, out *certificates.PodCertificateRequest, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - if err := Convert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(&in.Status, &out.Status, s); err != nil { - return err - } - return nil -} - -// Convert_v1alpha1_PodCertificateRequest_To_certificates_PodCertificateRequest is an autogenerated conversion function. -func Convert_v1alpha1_PodCertificateRequest_To_certificates_PodCertificateRequest(in *certificatesv1alpha1.PodCertificateRequest, out *certificates.PodCertificateRequest, s conversion.Scope) error { - return autoConvert_v1alpha1_PodCertificateRequest_To_certificates_PodCertificateRequest(in, out, s) -} - -func autoConvert_certificates_PodCertificateRequest_To_v1alpha1_PodCertificateRequest(in *certificates.PodCertificateRequest, out *certificatesv1alpha1.PodCertificateRequest, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - if err := Convert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus(&in.Status, &out.Status, s); err != nil { - return err - } - return nil -} - -// Convert_certificates_PodCertificateRequest_To_v1alpha1_PodCertificateRequest is an autogenerated conversion function. -func Convert_certificates_PodCertificateRequest_To_v1alpha1_PodCertificateRequest(in *certificates.PodCertificateRequest, out *certificatesv1alpha1.PodCertificateRequest, s conversion.Scope) error { - return autoConvert_certificates_PodCertificateRequest_To_v1alpha1_PodCertificateRequest(in, out, s) -} - -func autoConvert_v1alpha1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in *certificatesv1alpha1.PodCertificateRequestList, out *certificates.PodCertificateRequestList, s conversion.Scope) error { - out.ListMeta = in.ListMeta - out.Items = *(*[]certificates.PodCertificateRequest)(unsafe.Pointer(&in.Items)) - return nil -} - -// Convert_v1alpha1_PodCertificateRequestList_To_certificates_PodCertificateRequestList is an autogenerated conversion function. -func Convert_v1alpha1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in *certificatesv1alpha1.PodCertificateRequestList, out *certificates.PodCertificateRequestList, s conversion.Scope) error { - return autoConvert_v1alpha1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in, out, s) -} - -func autoConvert_certificates_PodCertificateRequestList_To_v1alpha1_PodCertificateRequestList(in *certificates.PodCertificateRequestList, out *certificatesv1alpha1.PodCertificateRequestList, s conversion.Scope) error { - out.ListMeta = in.ListMeta - out.Items = *(*[]certificatesv1alpha1.PodCertificateRequest)(unsafe.Pointer(&in.Items)) - return nil -} - -// Convert_certificates_PodCertificateRequestList_To_v1alpha1_PodCertificateRequestList is an autogenerated conversion function. -func Convert_certificates_PodCertificateRequestList_To_v1alpha1_PodCertificateRequestList(in *certificates.PodCertificateRequestList, out *certificatesv1alpha1.PodCertificateRequestList, s conversion.Scope) error { - return autoConvert_certificates_PodCertificateRequestList_To_v1alpha1_PodCertificateRequestList(in, out, s) -} - -func autoConvert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in *certificatesv1alpha1.PodCertificateRequestSpec, out *certificates.PodCertificateRequestSpec, s conversion.Scope) error { - out.SignerName = in.SignerName - out.PodName = in.PodName - out.PodUID = types.UID(in.PodUID) - out.ServiceAccountName = in.ServiceAccountName - out.ServiceAccountUID = types.UID(in.ServiceAccountUID) - out.NodeName = types.NodeName(in.NodeName) - out.NodeUID = types.UID(in.NodeUID) - out.MaxExpirationSeconds = (*int32)(unsafe.Pointer(in.MaxExpirationSeconds)) - out.PKIXPublicKey = *(*[]byte)(unsafe.Pointer(&in.PKIXPublicKey)) - out.ProofOfPossession = *(*[]byte)(unsafe.Pointer(&in.ProofOfPossession)) - return nil -} - -// Convert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec is an autogenerated conversion function. -func Convert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in *certificatesv1alpha1.PodCertificateRequestSpec, out *certificates.PodCertificateRequestSpec, s conversion.Scope) error { - return autoConvert_v1alpha1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in, out, s) -} - -func autoConvert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec(in *certificates.PodCertificateRequestSpec, out *certificatesv1alpha1.PodCertificateRequestSpec, s conversion.Scope) error { - out.SignerName = in.SignerName - out.PodName = in.PodName - out.PodUID = types.UID(in.PodUID) - out.ServiceAccountName = in.ServiceAccountName - out.ServiceAccountUID = types.UID(in.ServiceAccountUID) - out.NodeName = types.NodeName(in.NodeName) - out.NodeUID = types.UID(in.NodeUID) - out.MaxExpirationSeconds = (*int32)(unsafe.Pointer(in.MaxExpirationSeconds)) - out.PKIXPublicKey = *(*[]byte)(unsafe.Pointer(&in.PKIXPublicKey)) - out.ProofOfPossession = *(*[]byte)(unsafe.Pointer(&in.ProofOfPossession)) - return nil -} - -// Convert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec is an autogenerated conversion function. -func Convert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec(in *certificates.PodCertificateRequestSpec, out *certificatesv1alpha1.PodCertificateRequestSpec, s conversion.Scope) error { - return autoConvert_certificates_PodCertificateRequestSpec_To_v1alpha1_PodCertificateRequestSpec(in, out, s) -} - -func autoConvert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in *certificatesv1alpha1.PodCertificateRequestStatus, out *certificates.PodCertificateRequestStatus, s conversion.Scope) error { - out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) - out.CertificateChain = in.CertificateChain - out.NotBefore = (*v1.Time)(unsafe.Pointer(in.NotBefore)) - out.BeginRefreshAt = (*v1.Time)(unsafe.Pointer(in.BeginRefreshAt)) - out.NotAfter = (*v1.Time)(unsafe.Pointer(in.NotAfter)) - return nil -} - -// Convert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus is an autogenerated conversion function. -func Convert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in *certificatesv1alpha1.PodCertificateRequestStatus, out *certificates.PodCertificateRequestStatus, s conversion.Scope) error { - return autoConvert_v1alpha1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in, out, s) -} - -func autoConvert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus(in *certificates.PodCertificateRequestStatus, out *certificatesv1alpha1.PodCertificateRequestStatus, s conversion.Scope) error { - out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) - out.CertificateChain = in.CertificateChain - out.NotBefore = (*v1.Time)(unsafe.Pointer(in.NotBefore)) - out.BeginRefreshAt = (*v1.Time)(unsafe.Pointer(in.BeginRefreshAt)) - out.NotAfter = (*v1.Time)(unsafe.Pointer(in.NotAfter)) - return nil -} - -// Convert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus is an autogenerated conversion function. -func Convert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus(in *certificates.PodCertificateRequestStatus, out *certificatesv1alpha1.PodCertificateRequestStatus, s conversion.Scope) error { - return autoConvert_certificates_PodCertificateRequestStatus_To_v1alpha1_PodCertificateRequestStatus(in, out, s) -} diff --git a/pkg/apis/certificates/v1alpha1/zz_generated.defaults.go b/pkg/apis/certificates/v1alpha1/zz_generated.defaults.go index bc8d9f8c2fe1d..5070cb91b90f1 100644 --- a/pkg/apis/certificates/v1alpha1/zz_generated.defaults.go +++ b/pkg/apis/certificates/v1alpha1/zz_generated.defaults.go @@ -22,7 +22,6 @@ limitations under the License. package v1alpha1 import ( - certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -30,25 +29,5 @@ import ( // Public to allow building arbitrary schemes. // All generated defaulters are covering - they call all nested defaulters. func RegisterDefaults(scheme *runtime.Scheme) error { - scheme.AddTypeDefaultingFunc(&certificatesv1alpha1.PodCertificateRequest{}, func(obj interface{}) { - SetObjectDefaults_PodCertificateRequest(obj.(*certificatesv1alpha1.PodCertificateRequest)) - }) - scheme.AddTypeDefaultingFunc(&certificatesv1alpha1.PodCertificateRequestList{}, func(obj interface{}) { - SetObjectDefaults_PodCertificateRequestList(obj.(*certificatesv1alpha1.PodCertificateRequestList)) - }) return nil } - -func SetObjectDefaults_PodCertificateRequest(in *certificatesv1alpha1.PodCertificateRequest) { - if in.Spec.MaxExpirationSeconds == nil { - var ptrVar1 int32 = 86400 - in.Spec.MaxExpirationSeconds = &ptrVar1 - } -} - -func SetObjectDefaults_PodCertificateRequestList(in *certificatesv1alpha1.PodCertificateRequestList) { - for i := range in.Items { - a := &in.Items[i] - SetObjectDefaults_PodCertificateRequest(a) - } -} diff --git a/pkg/apis/certificates/v1beta1/conversion.go b/pkg/apis/certificates/v1beta1/conversion.go index f7fb6907a7373..a269a36c41e64 100644 --- a/pkg/apis/certificates/v1beta1/conversion.go +++ b/pkg/apis/certificates/v1beta1/conversion.go @@ -39,15 +39,35 @@ func addConversionFuncs(scheme *runtime.Scheme) error { return err } - return scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("ClusterTrustBundle"), + err = scheme.AddFieldLabelConversionFunc( + SchemeGroupVersion.WithKind("ClusterTrustBundle"), func(label, value string) (string, string, error) { switch label { - case "metadata.name", - "spec.signerName": + case "metadata.name", "spec.signerName": + return label, value, nil + default: + return "", "", fmt.Errorf("field label not supported: %s", label) + } + }, + ) + if err != nil { + return fmt.Errorf("while adding ClusterTrustBundle field label conversion func: %w", err) + } + + err = scheme.AddFieldLabelConversionFunc( + SchemeGroupVersion.WithKind("PodCertificateRequest"), + func(label, value string) (string, string, error) { + switch label { + case "metadata.name", "spec.signerName", "spec.podName", "spec.nodeName": return label, value, nil default: return "", "", fmt.Errorf("field label not supported: %s", label) } }, ) + if err != nil { + return fmt.Errorf("while adding PodCertificateRequest field label conversion func: %w", err) + } + + return nil } diff --git a/pkg/apis/certificates/v1beta1/zz_generated.conversion.go b/pkg/apis/certificates/v1beta1/zz_generated.conversion.go index 2bdc84a2c9e27..32315f49b16ba 100644 --- a/pkg/apis/certificates/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/certificates/v1beta1/zz_generated.conversion.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" + types "k8s.io/apimachinery/pkg/types" certificates "k8s.io/kubernetes/pkg/apis/certificates" core "k8s.io/kubernetes/pkg/apis/core" ) @@ -120,6 +121,46 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*certificatesv1beta1.PodCertificateRequest)(nil), (*certificates.PodCertificateRequest)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_PodCertificateRequest_To_certificates_PodCertificateRequest(a.(*certificatesv1beta1.PodCertificateRequest), b.(*certificates.PodCertificateRequest), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequest)(nil), (*certificatesv1beta1.PodCertificateRequest)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certificates_PodCertificateRequest_To_v1beta1_PodCertificateRequest(a.(*certificates.PodCertificateRequest), b.(*certificatesv1beta1.PodCertificateRequest), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificatesv1beta1.PodCertificateRequestList)(nil), (*certificates.PodCertificateRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(a.(*certificatesv1beta1.PodCertificateRequestList), b.(*certificates.PodCertificateRequestList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestList)(nil), (*certificatesv1beta1.PodCertificateRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certificates_PodCertificateRequestList_To_v1beta1_PodCertificateRequestList(a.(*certificates.PodCertificateRequestList), b.(*certificatesv1beta1.PodCertificateRequestList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificatesv1beta1.PodCertificateRequestSpec)(nil), (*certificates.PodCertificateRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(a.(*certificatesv1beta1.PodCertificateRequestSpec), b.(*certificates.PodCertificateRequestSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestSpec)(nil), (*certificatesv1beta1.PodCertificateRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec(a.(*certificates.PodCertificateRequestSpec), b.(*certificatesv1beta1.PodCertificateRequestSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificatesv1beta1.PodCertificateRequestStatus)(nil), (*certificates.PodCertificateRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(a.(*certificatesv1beta1.PodCertificateRequestStatus), b.(*certificates.PodCertificateRequestStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*certificates.PodCertificateRequestStatus)(nil), (*certificatesv1beta1.PodCertificateRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus(a.(*certificates.PodCertificateRequestStatus), b.(*certificatesv1beta1.PodCertificateRequestStatus), scope) + }); err != nil { + return err + } return nil } @@ -356,3 +397,125 @@ func autoConvert_certificates_ClusterTrustBundleSpec_To_v1beta1_ClusterTrustBund func Convert_certificates_ClusterTrustBundleSpec_To_v1beta1_ClusterTrustBundleSpec(in *certificates.ClusterTrustBundleSpec, out *certificatesv1beta1.ClusterTrustBundleSpec, s conversion.Scope) error { return autoConvert_certificates_ClusterTrustBundleSpec_To_v1beta1_ClusterTrustBundleSpec(in, out, s) } + +func autoConvert_v1beta1_PodCertificateRequest_To_certificates_PodCertificateRequest(in *certificatesv1beta1.PodCertificateRequest, out *certificates.PodCertificateRequest, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_PodCertificateRequest_To_certificates_PodCertificateRequest is an autogenerated conversion function. +func Convert_v1beta1_PodCertificateRequest_To_certificates_PodCertificateRequest(in *certificatesv1beta1.PodCertificateRequest, out *certificates.PodCertificateRequest, s conversion.Scope) error { + return autoConvert_v1beta1_PodCertificateRequest_To_certificates_PodCertificateRequest(in, out, s) +} + +func autoConvert_certificates_PodCertificateRequest_To_v1beta1_PodCertificateRequest(in *certificates.PodCertificateRequest, out *certificatesv1beta1.PodCertificateRequest, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_certificates_PodCertificateRequest_To_v1beta1_PodCertificateRequest is an autogenerated conversion function. +func Convert_certificates_PodCertificateRequest_To_v1beta1_PodCertificateRequest(in *certificates.PodCertificateRequest, out *certificatesv1beta1.PodCertificateRequest, s conversion.Scope) error { + return autoConvert_certificates_PodCertificateRequest_To_v1beta1_PodCertificateRequest(in, out, s) +} + +func autoConvert_v1beta1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in *certificatesv1beta1.PodCertificateRequestList, out *certificates.PodCertificateRequestList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]certificates.PodCertificateRequest)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta1_PodCertificateRequestList_To_certificates_PodCertificateRequestList is an autogenerated conversion function. +func Convert_v1beta1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in *certificatesv1beta1.PodCertificateRequestList, out *certificates.PodCertificateRequestList, s conversion.Scope) error { + return autoConvert_v1beta1_PodCertificateRequestList_To_certificates_PodCertificateRequestList(in, out, s) +} + +func autoConvert_certificates_PodCertificateRequestList_To_v1beta1_PodCertificateRequestList(in *certificates.PodCertificateRequestList, out *certificatesv1beta1.PodCertificateRequestList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]certificatesv1beta1.PodCertificateRequest)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_certificates_PodCertificateRequestList_To_v1beta1_PodCertificateRequestList is an autogenerated conversion function. +func Convert_certificates_PodCertificateRequestList_To_v1beta1_PodCertificateRequestList(in *certificates.PodCertificateRequestList, out *certificatesv1beta1.PodCertificateRequestList, s conversion.Scope) error { + return autoConvert_certificates_PodCertificateRequestList_To_v1beta1_PodCertificateRequestList(in, out, s) +} + +func autoConvert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in *certificatesv1beta1.PodCertificateRequestSpec, out *certificates.PodCertificateRequestSpec, s conversion.Scope) error { + out.SignerName = in.SignerName + out.PodName = in.PodName + out.PodUID = types.UID(in.PodUID) + out.ServiceAccountName = in.ServiceAccountName + out.ServiceAccountUID = types.UID(in.ServiceAccountUID) + out.NodeName = types.NodeName(in.NodeName) + out.NodeUID = types.UID(in.NodeUID) + out.MaxExpirationSeconds = (*int32)(unsafe.Pointer(in.MaxExpirationSeconds)) + out.PKIXPublicKey = *(*[]byte)(unsafe.Pointer(&in.PKIXPublicKey)) + out.ProofOfPossession = *(*[]byte)(unsafe.Pointer(&in.ProofOfPossession)) + out.UnverifiedUserAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UnverifiedUserAnnotations)) + return nil +} + +// Convert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec is an autogenerated conversion function. +func Convert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in *certificatesv1beta1.PodCertificateRequestSpec, out *certificates.PodCertificateRequestSpec, s conversion.Scope) error { + return autoConvert_v1beta1_PodCertificateRequestSpec_To_certificates_PodCertificateRequestSpec(in, out, s) +} + +func autoConvert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec(in *certificates.PodCertificateRequestSpec, out *certificatesv1beta1.PodCertificateRequestSpec, s conversion.Scope) error { + out.SignerName = in.SignerName + out.PodName = in.PodName + out.PodUID = types.UID(in.PodUID) + out.ServiceAccountName = in.ServiceAccountName + out.ServiceAccountUID = types.UID(in.ServiceAccountUID) + out.NodeName = types.NodeName(in.NodeName) + out.NodeUID = types.UID(in.NodeUID) + out.MaxExpirationSeconds = (*int32)(unsafe.Pointer(in.MaxExpirationSeconds)) + out.PKIXPublicKey = *(*[]byte)(unsafe.Pointer(&in.PKIXPublicKey)) + out.ProofOfPossession = *(*[]byte)(unsafe.Pointer(&in.ProofOfPossession)) + out.UnverifiedUserAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UnverifiedUserAnnotations)) + return nil +} + +// Convert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec is an autogenerated conversion function. +func Convert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec(in *certificates.PodCertificateRequestSpec, out *certificatesv1beta1.PodCertificateRequestSpec, s conversion.Scope) error { + return autoConvert_certificates_PodCertificateRequestSpec_To_v1beta1_PodCertificateRequestSpec(in, out, s) +} + +func autoConvert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in *certificatesv1beta1.PodCertificateRequestStatus, out *certificates.PodCertificateRequestStatus, s conversion.Scope) error { + out.Conditions = *(*[]metav1.Condition)(unsafe.Pointer(&in.Conditions)) + out.CertificateChain = in.CertificateChain + out.NotBefore = (*metav1.Time)(unsafe.Pointer(in.NotBefore)) + out.BeginRefreshAt = (*metav1.Time)(unsafe.Pointer(in.BeginRefreshAt)) + out.NotAfter = (*metav1.Time)(unsafe.Pointer(in.NotAfter)) + return nil +} + +// Convert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus is an autogenerated conversion function. +func Convert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in *certificatesv1beta1.PodCertificateRequestStatus, out *certificates.PodCertificateRequestStatus, s conversion.Scope) error { + return autoConvert_v1beta1_PodCertificateRequestStatus_To_certificates_PodCertificateRequestStatus(in, out, s) +} + +func autoConvert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus(in *certificates.PodCertificateRequestStatus, out *certificatesv1beta1.PodCertificateRequestStatus, s conversion.Scope) error { + out.Conditions = *(*[]metav1.Condition)(unsafe.Pointer(&in.Conditions)) + out.CertificateChain = in.CertificateChain + out.NotBefore = (*metav1.Time)(unsafe.Pointer(in.NotBefore)) + out.BeginRefreshAt = (*metav1.Time)(unsafe.Pointer(in.BeginRefreshAt)) + out.NotAfter = (*metav1.Time)(unsafe.Pointer(in.NotAfter)) + return nil +} + +// Convert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus is an autogenerated conversion function. +func Convert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus(in *certificates.PodCertificateRequestStatus, out *certificatesv1beta1.PodCertificateRequestStatus, s conversion.Scope) error { + return autoConvert_certificates_PodCertificateRequestStatus_To_v1beta1_PodCertificateRequestStatus(in, out, s) +} diff --git a/pkg/apis/certificates/v1beta1/zz_generated.defaults.go b/pkg/apis/certificates/v1beta1/zz_generated.defaults.go index 8f7d90d12f800..9a7c907d7d704 100644 --- a/pkg/apis/certificates/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/certificates/v1beta1/zz_generated.defaults.go @@ -36,6 +36,12 @@ func RegisterDefaults(scheme *runtime.Scheme) error { scheme.AddTypeDefaultingFunc(&certificatesv1beta1.CertificateSigningRequestList{}, func(obj interface{}) { SetObjectDefaults_CertificateSigningRequestList(obj.(*certificatesv1beta1.CertificateSigningRequestList)) }) + scheme.AddTypeDefaultingFunc(&certificatesv1beta1.PodCertificateRequest{}, func(obj interface{}) { + SetObjectDefaults_PodCertificateRequest(obj.(*certificatesv1beta1.PodCertificateRequest)) + }) + scheme.AddTypeDefaultingFunc(&certificatesv1beta1.PodCertificateRequestList{}, func(obj interface{}) { + SetObjectDefaults_PodCertificateRequestList(obj.(*certificatesv1beta1.PodCertificateRequestList)) + }) return nil } @@ -53,3 +59,17 @@ func SetObjectDefaults_CertificateSigningRequestList(in *certificatesv1beta1.Cer SetObjectDefaults_CertificateSigningRequest(a) } } + +func SetObjectDefaults_PodCertificateRequest(in *certificatesv1beta1.PodCertificateRequest) { + if in.Spec.MaxExpirationSeconds == nil { + var ptrVar1 int32 = 86400 + in.Spec.MaxExpirationSeconds = &ptrVar1 + } +} + +func SetObjectDefaults_PodCertificateRequestList(in *certificatesv1beta1.PodCertificateRequestList) { + for i := range in.Items { + a := &in.Items[i] + SetObjectDefaults_PodCertificateRequest(a) + } +} diff --git a/pkg/apis/certificates/v1beta1/zz_generated.validations.go b/pkg/apis/certificates/v1beta1/zz_generated.validations.go index 99d3dc0267b10..f103482ea7c3e 100644 --- a/pkg/apis/certificates/v1beta1/zz_generated.validations.go +++ b/pkg/apis/certificates/v1beta1/zz_generated.validations.go @@ -39,6 +39,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type CertificateSigningRequest scheme.AddValidationFunc((*certificatesv1beta1.CertificateSigningRequest)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/", "/approval", "/status": @@ -46,6 +47,7 @@ func RegisterValidations(scheme *runtime.Scheme) error { } return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} }) + // type CertificateSigningRequestList scheme.AddValidationFunc((*certificatesv1beta1.CertificateSigningRequestList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/": @@ -56,6 +58,8 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_CertificateSigningRequest validates an instance of CertificateSigningRequest according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1beta1.CertificateSigningRequest) (errs field.ErrorList) { // field certificatesv1beta1.CertificateSigningRequest.TypeMeta has no validation // field certificatesv1beta1.CertificateSigningRequest.ObjectMeta has no validation @@ -63,48 +67,66 @@ func Validate_CertificateSigningRequest(ctx context.Context, op operation.Operat // field certificatesv1beta1.CertificateSigningRequest.Status errs = append(errs, - func(fldPath *field.Path, obj, oldObj *certificatesv1beta1.CertificateSigningRequestStatus) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *certificatesv1beta1.CertificateSigningRequestStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function errs = append(errs, Validate_CertificateSigningRequestStatus(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *certificatesv1beta1.CertificateSigningRequest) *certificatesv1beta1.CertificateSigningRequestStatus { return &oldObj.Status - }))...) + }), oldObj != nil)...) return errs } +// Validate_CertificateSigningRequestList validates an instance of CertificateSigningRequestList according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequestList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1beta1.CertificateSigningRequestList) (errs field.ErrorList) { // field certificatesv1beta1.CertificateSigningRequestList.TypeMeta has no validation // field certificatesv1beta1.CertificateSigningRequestList.ListMeta has no validation // field certificatesv1beta1.CertificateSigningRequestList.Items errs = append(errs, - func(fldPath *field.Path, obj, oldObj []certificatesv1beta1.CertificateSigningRequest) (errs field.ErrorList) { - if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj []certificatesv1beta1.CertificateSigningRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil } + // iterate the list and call the type's validation function errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_CertificateSigningRequest)...) return }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *certificatesv1beta1.CertificateSigningRequestList) []certificatesv1beta1.CertificateSigningRequest { return oldObj.Items - }))...) + }), oldObj != nil)...) return errs } -var zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1beta1_CertificateSigningRequestStatus_Conditions_ = validate.NewUnionMembership([2]string{"Conditions[{\"type\": \"Approved\"}]", ""}, [2]string{"Conditions[{\"type\": \"Denied\"}]", ""}) +var zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1beta1_CertificateSigningRequestStatus_conditions_ = validate.NewUnionMembership(validate.NewUnionMember("conditions[{\"type\": \"Approved\"}]"), validate.NewUnionMember("conditions[{\"type\": \"Denied\"}]")) +// Validate_CertificateSigningRequestStatus validates an instance of CertificateSigningRequestStatus according +// to declarative validation rules in the API schema. func Validate_CertificateSigningRequestStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *certificatesv1beta1.CertificateSigningRequestStatus) (errs field.ErrorList) { // field certificatesv1beta1.CertificateSigningRequestStatus.Conditions errs = append(errs, - func(fldPath *field.Path, obj, oldObj []certificatesv1beta1.CertificateSigningRequestCondition) (errs field.ErrorList) { - if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj []certificatesv1beta1.CertificateSigningRequestCondition, oldValueCorrelated bool) (errs field.ErrorList) { + // Uniqueness validation is implemented via custom, handwritten validation + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil } + // call field-attached validations + earlyReturn := false if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { return // do not proceed } - errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1beta1_CertificateSigningRequestStatus_Conditions_, func(list []certificatesv1beta1.CertificateSigningRequestCondition) bool { + errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_api_certificates_v1beta1_CertificateSigningRequestStatus_conditions_, func(list []certificatesv1beta1.CertificateSigningRequestCondition) bool { for i := range list { if list[i].Type == "Approved" { return true @@ -122,7 +144,7 @@ func Validate_CertificateSigningRequestStatus(ctx context.Context, op operation. return }(fldPath.Child("conditions"), obj.Conditions, safe.Field(oldObj, func(oldObj *certificatesv1beta1.CertificateSigningRequestStatus) []certificatesv1beta1.CertificateSigningRequestCondition { return oldObj.Conditions - }))...) + }), oldObj != nil)...) // field certificatesv1beta1.CertificateSigningRequestStatus.Certificate has no validation return errs diff --git a/pkg/apis/certificates/validation/validation.go b/pkg/apis/certificates/validation/validation.go index ad3ad80ee304f..0b2b6046bbdd6 100644 --- a/pkg/apis/certificates/validation/validation.go +++ b/pkg/apis/certificates/validation/validation.go @@ -27,7 +27,9 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "net/mail" "strconv" + "strings" "time" v1 "k8s.io/api/core/v1" @@ -595,6 +597,11 @@ func ValidatePodCertificateRequestCreate(req *certificates.PodCertificateRequest signerNameErrors := apivalidation.ValidateSignerName(field.NewPath("spec", "signerName"), req.Spec.SignerName) allErrors = append(allErrors, signerNameErrors...) + if req.Spec.UnverifiedUserAnnotations != nil { + userAnnotationsErrors := apivalidation.ValidateUserAnnotations(req.Spec.UnverifiedUserAnnotations, field.NewPath("spec", "unverifiedUserAnnotations")) + allErrors = append(allErrors, userAnnotationsErrors...) + } + for _, msg := range apivalidation.ValidatePodName(req.Spec.PodName, false) { allErrors = append(allErrors, field.Invalid(field.NewPath("spec", "podName"), req.Spec.PodName, msg)) } @@ -802,6 +809,22 @@ func ValidatePodCertificateRequestStatusUpdate(newReq, oldReq *certificates.PodC allErrors = append(allErrors, field.Invalid(certChainPath, newReq.Status.CertificateChain, "leaf certificate does not parse as valid X.509")) return allErrors } + for _, dnsName := range leafCert.DNSNames { + if dnsName == "" { + allErrors = append(allErrors, field.Invalid(certChainPath, dnsName, "leaf certificate should not contain empty DNSName")) + } + if strings.Contains(dnsName, "..") { + allErrors = append(allErrors, field.Invalid(certChainPath, dnsName, "leaf certificate's DNSName should not contain '..'")) + } + if strings.HasPrefix(dnsName, ".") || strings.HasSuffix(dnsName, ".") { + allErrors = append(allErrors, field.Invalid(certChainPath, dnsName, "leaf certificate's DNSName should not start or end with '.'")) + } + } + for _, emailAddress := range leafCert.EmailAddresses { + if _, err := mail.ParseAddress(emailAddress); err != nil { + allErrors = append(allErrors, field.Invalid(certChainPath, emailAddress, "leaf certificate should not contain invalid EmailAddress")) + } + } // Was the certificate issued to the public key in the spec? wantPKAny, err := x509.ParsePKIXPublicKey(oldReq.Spec.PKIXPublicKey) diff --git a/pkg/apis/certificates/validation/validation_test.go b/pkg/apis/certificates/validation/validation_test.go index d22c637c2fb27..717251d188f4e 100644 --- a/pkg/apis/certificates/validation/validation_test.go +++ b/pkg/apis/certificates/validation/validation_test.go @@ -36,6 +36,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" @@ -2217,6 +2218,81 @@ func TestValidatePodCertificateRequestCreate(t *testing.T) { field.TooLong(field.NewPath("spec", "proofOfPossession"), make([]byte, capi.MaxProofOfPossessionSize+1), capi.MaxProofOfPossessionSize), }, }, + { + description: "bad user annotations key name", + pcr: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: ed25519PubPKIX1, + ProofOfPossession: ed25519Proof1, + UnverifiedUserAnnotations: map[string]string{"test/domain/foo": "bar"}, + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("spec", "unverifiedUserAnnotations"), "test/domain/foo", "a valid label key must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')"), + }, + }, + { + description: "bad user annotations key prefix too long", + pcr: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: ed25519PubPKIX1, + ProofOfPossession: ed25519Proof1, + UnverifiedUserAnnotations: map[string]string{strings.Repeat("a", 254) + "/foo": "bar"}, + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("spec", "unverifiedUserAnnotations"), strings.Repeat("a", 254)+"/foo", "prefix part must be no more than 253 bytes"), + }, + }, + { + description: "bad user annotations key/value total size too long", + pcr: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: ed25519PubPKIX1, + ProofOfPossession: ed25519Proof1, + UnverifiedUserAnnotations: map[string]string{"foo/bar": strings.Repeat("d", apimachineryvalidation.TotalAnnotationSizeLimitB)}, + }, + }, + wantErrors: field.ErrorList{ + field.TooLong(field.NewPath("spec", "unverifiedUserAnnotations"), "", apimachineryvalidation.TotalAnnotationSizeLimitB), + }, + }, } for _, tc := range testCases { @@ -2248,16 +2324,17 @@ func TestValidatePodCertificateRequestUpdate(t *testing.T) { Name: "bar", }, Spec: capi.PodCertificateRequestSpec{ - SignerName: "foo.com/abc", - PodName: "pod-1", - PodUID: types.UID(podUID1), - ServiceAccountName: "sa-1", - ServiceAccountUID: "sa-uid-1", - NodeName: "node-1", - NodeUID: "node-uid-1", - MaxExpirationSeconds: ptr.To[int32](86400), - PKIXPublicKey: pubPKIX1, - ProofOfPossession: proof1, + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + UnverifiedUserAnnotations: map[string]string{"test.domain/foo": "bar"}, }, }, newPCR: &capi.PodCertificateRequest{ @@ -2266,32 +2343,34 @@ func TestValidatePodCertificateRequestUpdate(t *testing.T) { Name: "bar", }, Spec: capi.PodCertificateRequestSpec{ - SignerName: "foo.com/new", - PodName: "new", - PodUID: types.UID("new"), - ServiceAccountName: "new", - ServiceAccountUID: "new", - NodeName: "new", - NodeUID: "new", - MaxExpirationSeconds: ptr.To[int32](86401), - PKIXPublicKey: pubPKIX1, - ProofOfPossession: proof1, + SignerName: "foo.com/new", + PodName: "new", + PodUID: types.UID("new"), + ServiceAccountName: "new", + ServiceAccountUID: "new", + NodeName: "new", + NodeUID: "new", + MaxExpirationSeconds: ptr.To[int32](86401), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + UnverifiedUserAnnotations: map[string]string{"test.domain/foo": "foo"}, }, }, wantErrors: field.ErrorList{ field.Invalid( field.NewPath("spec"), capi.PodCertificateRequestSpec{ - SignerName: "foo.com/new", - PodName: "new", - PodUID: types.UID("new"), - ServiceAccountName: "new", - ServiceAccountUID: "new", - NodeName: "new", - NodeUID: "new", - MaxExpirationSeconds: ptr.To[int32](86401), - PKIXPublicKey: pubPKIX1, - ProofOfPossession: proof1, + SignerName: "foo.com/new", + PodName: "new", + PodUID: types.UID("new"), + ServiceAccountName: "new", + ServiceAccountUID: "new", + NodeName: "new", + NodeUID: "new", + MaxExpirationSeconds: ptr.To[int32](86401), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + UnverifiedUserAnnotations: map[string]string{"test.domain/foo": "foo"}, }, "field is immutable", ), @@ -2319,12 +2398,17 @@ func TestValidatePodCertificateRequestStatusUpdate(t *testing.T) { podUID1 := "pod-uid-1" _, pub1, pubPKIX1, proof1 := mustMakeEd25519KeyAndProof(t, []byte(podUID1)) - pod1Cert1 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey) - pod1Cert2 := mustSignCertForPublicKey(t, 18*time.Hour, pub1, caCertDER, caPrivKey) - badCertTooShort := mustSignCertForPublicKey(t, 50*time.Minute, pub1, caCertDER, caPrivKey) - badCertTooLong := mustSignCertForPublicKey(t, 25*time.Hour, pub1, caCertDER, caPrivKey) + pod1Cert1 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, false, "", "") + pod1Cert2 := mustSignCertForPublicKey(t, 18*time.Hour, pub1, caCertDER, caPrivKey, false, "", "") + badCertTooShort := mustSignCertForPublicKey(t, 50*time.Minute, pub1, caCertDER, caPrivKey, false, "", "") + badCertTooLong := mustSignCertForPublicKey(t, 25*time.Hour, pub1, caCertDER, caPrivKey, false, "", "") + certWithBadDNSName1 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, true, "", "") + certWithBadDNSName2 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, true, "test-name..example", "") + certWithBadDNSName3 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, true, ".example", "") + certWithBadDNSName4 := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, true, "example.", "") + certWithBadEmailAddress := mustSignCertForPublicKey(t, 24*time.Hour, pub1, caCertDER, caPrivKey, false, "", "email@@address") - certFromIntermediate := mustSignCertForPublicKey(t, 24*time.Hour, pub1, intermediateCACertDER, intermediateCAPrivKey) + certFromIntermediate := mustSignCertForPublicKey(t, 24*time.Hour, pub1, intermediateCACertDER, intermediateCAPrivKey, false, "", "") testCases := []struct { description string @@ -3997,6 +4081,291 @@ func TestValidatePodCertificateRequestStatusUpdate(t *testing.T) { field.Invalid(field.NewPath("status", "certificateChain"), 25*time.Hour, "leaf certificate lifetime must be <= spec.maxExpirationSeconds (86400)"), }, }, + { + description: "leaf cert can not contain empty DNSName", + oldPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + }, + newPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + Status: capi.PodCertificateRequestStatus{ + Conditions: []metav1.Condition{ + { + Type: capi.PodCertificateRequestConditionTypeIssued, + Status: metav1.ConditionTrue, + Reason: "Whatever", + Message: "Foo message", + LastTransitionTime: metav1.NewTime(time.Now()), + }, + }, + CertificateChain: certWithBadDNSName1, + NotBefore: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T00:00:00Z"))), + BeginRefreshAt: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T12:00:00Z"))), + NotAfter: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-02T00:00:00Z"))), + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("status", "certificateChain"), "", "leaf certificate should not contain empty DNSName"), + }, + }, + { + description: "leaf cert can not contain DNSName contains '..'", + oldPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + }, + newPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + Status: capi.PodCertificateRequestStatus{ + Conditions: []metav1.Condition{ + { + Type: capi.PodCertificateRequestConditionTypeIssued, + Status: metav1.ConditionTrue, + Reason: "Whatever", + Message: "Foo message", + LastTransitionTime: metav1.NewTime(time.Now()), + }, + }, + CertificateChain: certWithBadDNSName2, + NotBefore: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T00:00:00Z"))), + BeginRefreshAt: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T12:00:00Z"))), + NotAfter: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-02T00:00:00Z"))), + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("status", "certificateChain"), "test-name..example", "leaf certificate's DNSName should not contain '..'"), + }, + }, + { + description: "leaf cert can not contain DNSName start with '.'", + oldPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + }, + newPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + Status: capi.PodCertificateRequestStatus{ + Conditions: []metav1.Condition{ + { + Type: capi.PodCertificateRequestConditionTypeIssued, + Status: metav1.ConditionTrue, + Reason: "Whatever", + Message: "Foo message", + LastTransitionTime: metav1.NewTime(time.Now()), + }, + }, + CertificateChain: certWithBadDNSName3, + NotBefore: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T00:00:00Z"))), + BeginRefreshAt: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T12:00:00Z"))), + NotAfter: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-02T00:00:00Z"))), + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("status", "certificateChain"), ".example", "leaf certificate's DNSName should not start or end with '.'"), + }, + }, + { + description: "leaf cert can not contain DNSName end with '.'", + oldPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + }, + newPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + Status: capi.PodCertificateRequestStatus{ + Conditions: []metav1.Condition{ + { + Type: capi.PodCertificateRequestConditionTypeIssued, + Status: metav1.ConditionTrue, + Reason: "Whatever", + Message: "Foo message", + LastTransitionTime: metav1.NewTime(time.Now()), + }, + }, + CertificateChain: certWithBadDNSName4, + NotBefore: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T00:00:00Z"))), + BeginRefreshAt: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T12:00:00Z"))), + NotAfter: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-02T00:00:00Z"))), + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("status", "certificateChain"), "example.", "leaf certificate's DNSName should not start or end with '.'"), + }, + }, + { + description: "leaf cert can not contain bad email address", + oldPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + }, + newPCR: &capi.PodCertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "bar", + }, + Spec: capi.PodCertificateRequestSpec{ + SignerName: "foo.com/abc", + PodName: "pod-1", + PodUID: types.UID(podUID1), + ServiceAccountName: "sa-1", + ServiceAccountUID: "sa-uid-1", + NodeName: "node-1", + NodeUID: "node-uid-1", + MaxExpirationSeconds: ptr.To[int32](86400), + PKIXPublicKey: pubPKIX1, + ProofOfPossession: proof1, + }, + Status: capi.PodCertificateRequestStatus{ + Conditions: []metav1.Condition{ + { + Type: capi.PodCertificateRequestConditionTypeIssued, + Status: metav1.ConditionTrue, + Reason: "Whatever", + Message: "Foo message", + LastTransitionTime: metav1.NewTime(time.Now()), + }, + }, + CertificateChain: certWithBadEmailAddress, + NotBefore: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T00:00:00Z"))), + BeginRefreshAt: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-01T12:00:00Z"))), + NotAfter: ptr.To(metav1.NewTime(mustParseTime(t, "1970-01-02T00:00:00Z"))), + }, + }, + wantErrors: field.ErrorList{ + field.Invalid(field.NewPath("status", "certificateChain"), "email@@address", "leaf certificate should not contain invalid EmailAddress"), + }, + }, } for _, tc := range testCases { @@ -4114,7 +4483,7 @@ func mustMakeRSAKeyAndProof(t *testing.T, modulusSize int, toBeSigned []byte) (* return priv, &priv.PublicKey, pubPKIX, sig } -func mustSignCertForPublicKey(t *testing.T, validity time.Duration, subjectPublicKey crypto.PublicKey, caCertDER []byte, caPrivateKey crypto.PrivateKey) string { +func mustSignCertForPublicKey(t *testing.T, validity time.Duration, subjectPublicKey crypto.PublicKey, caCertDER []byte, caPrivateKey crypto.PrivateKey, usebadDNSName bool, badDNSName, badEmailAddress string) string { certTemplate := &x509.Certificate{ Subject: pkix.Name{ CommonName: "foo", @@ -4124,6 +4493,12 @@ func mustSignCertForPublicKey(t *testing.T, validity time.Duration, subjectPubli NotBefore: mustParseTime(t, "1970-01-01T00:00:00Z"), NotAfter: mustParseTime(t, "1970-01-01T00:00:00Z").Add(validity), } + if usebadDNSName { + certTemplate.DNSNames = append(certTemplate.DNSNames, badDNSName) + } + if badEmailAddress != "" { + certTemplate.EmailAddresses = append(certTemplate.EmailAddresses, badEmailAddress) + } caCert, err := x509.ParseCertificate(caCertDER) if err != nil { diff --git a/pkg/apis/certificates/zz_generated.deepcopy.go b/pkg/apis/certificates/zz_generated.deepcopy.go index 87927df41b1a2..9a526c246c6bb 100644 --- a/pkg/apis/certificates/zz_generated.deepcopy.go +++ b/pkg/apis/certificates/zz_generated.deepcopy.go @@ -359,6 +359,13 @@ func (in *PodCertificateRequestSpec) DeepCopyInto(out *PodCertificateRequestSpec *out = make([]byte, len(*in)) copy(*out, *in) } + if in.UnverifiedUserAnnotations != nil { + in, out := &in.UnverifiedUserAnnotations, &out.UnverifiedUserAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index f62c07024b6f0..1de0cf4495ca4 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -398,6 +398,7 @@ type PersistentVolumeSpec struct { VolumeMode *PersistentVolumeMode // NodeAffinity defines constraints that limit what nodes this volume can be accessed from. // This field influences the scheduling of pods that use this volume. + // This field is mutable if MutablePVNodeAffinity feature gate is enabled. // +optional NodeAffinity *VolumeNodeAffinity // Name of VolumeAttributesClass to which this persistent volume belongs. Empty value @@ -724,9 +725,6 @@ type PersistentVolumeClaimStatus struct { // should ignore the update for the purpose it was designed. For example - a controller that // only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid // resources associated with PVC. - // - // This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. - // +featureGate=RecoverVolumeExpansionFailure // +optional AllocatedResources ResourceList // AllocatedResourceStatuses stores status of resource being resized for the given PVC. @@ -762,9 +760,6 @@ type PersistentVolumeClaimStatus struct { // should ignore the update for the purpose it was designed. For example - a controller that // only is responsible for resizing capacity of the volume, should ignore PVC updates that change other valid // resources associated with PVC. - // - // This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. - // +featureGate=RecoverVolumeExpansionFailure // +mapType=granular // +optional AllocatedResourceStatuses map[ResourceName]ClaimResourceStatus @@ -1904,6 +1899,21 @@ type PodCertificateProjection struct { // Write the certificate chain at this path in the projected volume. CertificateChainPath string + + // userAnnotations allow pod authors to pass additional information to + // the signer implementation. Kubernetes does not restrict or validate this + // metadata in any way. + // + // These values are copied verbatim into the `spec.unverifiedUserAnnotations` field of + // the PodCertificateRequest objects that Kubelet creates. + // + // Entries are subject to the same validation as object metadata annotations, + // with the addition that all keys must be domain-prefixed. No restrictions + // are placed on values, except an overall size limitation on the entire field. + // + // Signers should document the keys and values they support. Signers should + // deny requests that contain keys they do not recognize. + UserAnnotations map[string]string } // ProjectedVolumeSource represents a projected volume source @@ -2153,8 +2163,6 @@ type VolumeMount struct { // None (or be unspecified, which defaults to None). // // If this field is not specified, it is treated as an equivalent of Disabled. - // - // +featureGate=RecursiveReadOnlyMounts // +optional RecursiveReadOnly *RecursiveReadOnlyMode // Required. If the path is not an absolute path (e.g. some/path) it @@ -2647,6 +2655,7 @@ type Container struct { // +optional Resources ResourceRequirements // Resources resize policy for the container. + // This field cannot be set on ephemeral containers. // +featureGate=InPlacePodVerticalScaling // +optional ResizePolicy []ContainerResizePolicy @@ -2670,7 +2679,6 @@ type Container struct { // container. Instead, the next init container starts immediately after this // init container is started, or after any startupProbe has successfully // completed. - // +featureGate=SidecarContainers // +optional RestartPolicy *ContainerRestartPolicy // Represents a list of rules to be checked to determine if the @@ -2758,7 +2766,6 @@ type LifecycleHandler struct { // +optional TCPSocket *TCPSocketAction // Sleep represents the duration that the container should sleep before being terminated. - // +featureGate=PodLifecycleSleepAction // +optional Sleep *SleepAction } @@ -3000,7 +3007,6 @@ type ContainerStatus struct { // Status of volume mounts. // +listType=atomic // +optional - // +featureGate=RecursiveReadOnlyMounts VolumeMounts []VolumeMountStatus // User represents user identity information initially attached to the first process of the container // +featureGate=SupplementalGroupsPolicy @@ -3148,6 +3154,8 @@ const ( // If both PodResizePending and PodResizeInProgress are set, it means that a new resize was // requested in the middle of a previous pod resize that is still in progress. PodResizeInProgress PodConditionType = "PodResizeInProgress" + // AllContainersRestarting indicates that all containers of the pod is being restarted. + AllContainersRestarting PodConditionType = "AllContainersRestarting" ) // PodCondition represents pod's condition @@ -3191,7 +3199,6 @@ type VolumeMountStatus struct { // RecursiveReadOnly must be set to Disabled, Enabled, or unspecified (for non-readonly mounts). // An IfPossible value in the original VolumeMount must be translated to Disabled or Enabled, // depending on the mount result. - // +featureGate=RecursiveReadOnlyMounts // +optional RecursiveReadOnly *RecursiveReadOnlyMode } @@ -3236,9 +3243,15 @@ type ContainerRestartRule struct { // container exits. type ContainerRestartRuleAction string -// The only valid action is Restart. +// These are valid restart rule actions. const ( + // The container will be restarted if the rule matches. Only valid on normal init container and + // regular containers. Not valid on sidecar containers and ephemeral containers. ContainerRestartRuleActionRestart ContainerRestartRuleAction = "Restart" + // All containers (except ephemeral containers) inside the pod will be terminated and restarted. + // Valid on normal init container, sidecar containers, and regular containers. Not valid on + // ephemeral containers. + ContainerRestartRuleActionRestartAllContainers ContainerRestartRuleAction = "RestartAllContainers" ) // ContainerRestartRuleOnExitCodes describes the condition @@ -3616,9 +3629,10 @@ type Toleration struct { // +optional Key string // Operator represents a key's relationship to the value. - // Valid operators are Exists and Equal. Defaults to Equal. + // Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. // Exists is equivalent to wildcard for value, so that a pod can // tolerate all taints of a particular category. + // Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). // +optional Operator TolerationOperator // Value is the taint value the toleration matches to. @@ -3644,6 +3658,8 @@ type TolerationOperator string const ( TolerationOpExists TolerationOperator = "Exists" TolerationOpEqual TolerationOperator = "Equal" + TolerationOpLt TolerationOperator = "Lt" + TolerationOpGt TolerationOperator = "Gt" ) // PodReadinessGate contains the reference to a pod condition @@ -3847,8 +3863,8 @@ type PodSpec struct { // will be made available to those containers which consume them // by name. // - // This is an alpha field and requires enabling the - // DynamicResourceAllocation feature gate. + // This is a stable field but requires that the + // DynamicResourceAllocation feature gate is enabled. // // This field is immutable. // @@ -3883,6 +3899,17 @@ type PodSpec struct { // +featureGate=HostnameOverride // +optional HostnameOverride *string + // WorkloadRef provides a reference to the Workload object that this Pod belongs to. + // This field is used by the scheduler to identify the PodGroup and apply the + // correct group scheduling policies. The Workload object referenced + // by this field may not exist at the time the Pod is created. + // This field is immutable, but a Workload object with the same name + // may be recreated with different policies. Doing this during pod scheduling + // may result in the placement not conforming to the expected policies. + // + // +featureGate=GenericWorkload + // +optional + WorkloadRef *WorkloadReference } // PodResourceClaim references exactly one ResourceClaim through a ClaimSource. @@ -3981,6 +4008,36 @@ type PodSchedulingGate struct { Name string } +// WorkloadReference identifies the Workload object and PodGroup membership +// that a Pod belongs to. The scheduler uses this information to apply +// workload-aware scheduling semantics. +type WorkloadReference struct { + // Name defines the name of the Workload object this Pod belongs to. + // Workload must be in the same namespace as the Pod. + // If it doesn't match any existing Workload, the Pod will remain unschedulable + // until a Workload object is created and observed by the kube-scheduler. + // It must be a DNS subdomain. + // + // +required + Name string + + // PodGroup is the name of the PodGroup within the Workload that this Pod + // belongs to. If it doesn't match any existing PodGroup within the Workload, + // the Pod will remain unschedulable until the Workload object is recreated + // and observed by the kube-scheduler. It must be a DNS label. + // + // +required + PodGroup string + + // PodGroupReplicaKey specifies the replica key of the PodGroup to which this + // Pod belongs. It is used to distinguish pods belonging to different replicas + // of the same pod group. The pod group policy is applied separately to each replica. + // When set, it must be a DNS label. + // + // +optional + PodGroupReplicaKey string +} + // HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the // pod's hosts file. type HostAlias struct { @@ -4372,7 +4429,6 @@ type EphemeralContainerCommon struct { // Restart policy for the container to manage the restart behavior of each // container within a pod. Must be specified if restartPolicyRules are used. // You cannot set this field on ephemeral containers. - // +featureGate=SidecarContainers // +optional RestartPolicy *ContainerRestartPolicy // Represents a list of rules to be checked to determine if the @@ -4461,7 +4517,7 @@ type EphemeralContainer struct { // state of a system. type PodStatus struct { // If set, this represents the .metadata.generation that the pod status was set based upon. - // This is an alpha field. Enable PodObservedGenerationTracking to be able to use this field. + // The PodObservedGenerationTracking feature gate must be enabled to use this field. // +featureGate=PodObservedGenerationTracking // +optional ObservedGeneration int64 @@ -4562,6 +4618,19 @@ type PodStatus struct { // +featureGate=DRAExtendedResource // +optional ExtendedResourceClaimStatus *PodExtendedResourceClaimStatus + + // AllocatedResources is the total requests allocated for this pod by the node. + // If pod-level requests are not set, this will be the total requests aggregated + // across containers in the pod. + // +featureGate=InPlacePodLevelResourcesVerticalScaling + // +optional + AllocatedResources ResourceList + + // Resources represents the compute resource requests and limits that have been + // applied at the pod level + // +featureGate=InPlacePodLevelResourcesVerticalScaling + // +optional + Resources *ResourceRequirements } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -4851,27 +4920,25 @@ const ( // These are valid values for the TrafficDistribution field of a Service. const ( - // Indicates a preference for routing traffic to endpoints that are in the same - // zone as the client. Users should not set this value unless they have ensured - // that clients and endpoints are distributed in such a way that the "same zone" - // preference will not result in endpoints getting overloaded. - ServiceTrafficDistributionPreferClose = "PreferClose" - - // Indicates a preference for routing traffic to endpoints that are in the same - // zone as the client. Users should not set this value unless they have ensured - // that clients and endpoints are distributed in such a way that the "same zone" - // preference will not result in endpoints getting overloaded. - // This is an alias for "PreferClose", but it is an Alpha feature and is only - // recognized if the PreferSameTrafficDistribution feature gate is enabled. + // ServiceTrafficDistributionPreferSameZone indicates a preference for routing + // traffic to endpoints that are in the same zone as the client. Users should only + // set this value if they have ensured that clients and endpoints are distributed + // in such a way that the "same zone" preference will not result in endpoints + // getting overloaded. ServiceTrafficDistributionPreferSameZone = "PreferSameZone" - // Indicates a preference for routing traffic to endpoints that are on the same - // node as the client. Users should not set this value unless they have ensured - // that clients and endpoints are distributed in such a way that the "same node" - // preference will not result in endpoints getting overloaded. - // This is an Alpha feature and is only recognized if the - // PreferSameTrafficDistribution feature gate is enabled. + // ServiceTrafficDistributionPreferSameNode indicates a preference for routing + // traffic to endpoints that are on the same node as the client. Users should only + // set this value if they have ensured that clients and endpoints are distributed + // in such a way that the "same node" preference will not result in endpoints + // getting overloaded. ServiceTrafficDistributionPreferSameNode = "PreferSameNode" + + // ServiceTrafficDistributionPreferClose is the original name of "PreferSameZone". + // Despite the generic-sounding name, it has exactly the same meaning as + // "PreferSameZone". + // Deprecated: use "PreferSameZone" instead. + ServiceTrafficDistributionPreferClose = "PreferClose" ) // These are the valid conditions of a service. @@ -5439,7 +5506,6 @@ type NodeDaemonEndpoints struct { // NodeRuntimeHandlerFeatures is a set of features implemented by the runtime handler. type NodeRuntimeHandlerFeatures struct { // RecursiveReadOnlyMounts is set to true if the runtime handler supports RecursiveReadOnlyMounts. - // +featureGate=RecursiveReadOnlyMounts // +optional RecursiveReadOnlyMounts *bool // UserNamespaces is set to true if the runtime handler supports UserNamespaces, including for volumes. @@ -5588,7 +5654,6 @@ type NodeStatus struct { // +optional Config *NodeConfigStatus // The available runtime handlers. - // +featureGate=RecursiveReadOnlyMounts // +featureGate=UserNamespacesSupport // +optional RuntimeHandlers []NodeRuntimeHandler @@ -5596,6 +5661,10 @@ type NodeStatus struct { // +featureGate=SupplementalGroupsPolicy // +optional Features *NodeFeatures + // DeclaredFeatures represents the declared features of a node. + // +featureGate=NodeDeclaredFeatures + // +optional + DeclaredFeatures []string } // UniqueVolumeName defines the name of attached volume diff --git a/pkg/apis/core/v1/defaults.go b/pkg/apis/core/v1/defaults.go index 74dbc832d4ab3..a6b5e551bb89e 100644 --- a/pkg/apis/core/v1/defaults.go +++ b/pkg/apis/core/v1/defaults.go @@ -152,13 +152,10 @@ func SetDefaults_Service(obj *v1.Service) { } if obj.Spec.Type == v1.ServiceTypeLoadBalancer { - if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) { - ipMode := v1.LoadBalancerIPModeVIP - - for i, ing := range obj.Status.LoadBalancer.Ingress { - if ing.IP != "" && ing.IPMode == nil { - obj.Status.LoadBalancer.Ingress[i].IPMode = &ipMode - } + ipMode := v1.LoadBalancerIPModeVIP + for i, ing := range obj.Status.LoadBalancer.Ingress { + if ing.IP != "" && ing.IPMode == nil { + obj.Status.LoadBalancer.Ingress[i].IPMode = &ipMode } } } diff --git a/pkg/apis/core/v1/defaults_test.go b/pkg/apis/core/v1/defaults_test.go index 9f070a49650c6..dc0a0baccc515 100644 --- a/pkg/apis/core/v1/defaults_test.go +++ b/pkg/apis/core/v1/defaults_test.go @@ -31,7 +31,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/version" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -49,12 +48,7 @@ func TestWorkloadDefaults(t *testing.T) { t.Run("disabled_features", func(t *testing.T) { testWorkloadDefaults(t, false) }) } func testWorkloadDefaults(t *testing.T, featuresEnabled bool) { - allFeatures := utilfeature.DefaultFeatureGate.DeepCopy().GetAll() - for feature, featureSpec := range allFeatures { - if !featureSpec.LockToDefault { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled) - } - } + setAllFeatures(t, featuresEnabled) // New defaults under PodTemplateSpec are only acceptable if they would not be applied when reading data from a previous release. // Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value // Forbidden: defaulting an existing field `MyField *bool` when it was previously not defaulted @@ -240,12 +234,7 @@ func TestPodDefaults(t *testing.T) { t.Run("disabled_features", func(t *testing.T) { testPodDefaults(t, false) }) } func testPodDefaults(t *testing.T, featuresEnabled bool) { - features := utilfeature.DefaultFeatureGate.DeepCopy().GetAll() - for feature, featureSpec := range features { - if !featureSpec.LockToDefault { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled) - } - } + setAllFeatures(t, featuresEnabled) pod := &v1.Pod{} // New defaults under PodSpec are only acceptable if they would not be applied when reading data from a previous release. // Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value @@ -2447,26 +2436,11 @@ func TestSetDefaultServiceLoadbalancerIPMode(t *testing.T) { modeProxy := v1.LoadBalancerIPModeProxy testCases := []struct { name string - ipModeEnabled bool svc *v1.Service expectedIPMode []*v1.LoadBalancerIPMode }{ { - name: "Set IP but not set IPMode with LoadbalancerIPMode disabled", - ipModeEnabled: false, - svc: &v1.Service{ - Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, - Status: v1.ServiceStatus{ - LoadBalancer: v1.LoadBalancerStatus{ - Ingress: []v1.LoadBalancerIngress{{ - IP: "1.2.3.4", - }}, - }, - }}, - expectedIPMode: []*v1.LoadBalancerIPMode{nil}, - }, { - name: "Set IP but bot set IPMode with LoadbalancerIPMode enabled", - ipModeEnabled: true, + name: "Set IP but not set IPMode", svc: &v1.Service{ Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, Status: v1.ServiceStatus{ @@ -2478,8 +2452,7 @@ func TestSetDefaultServiceLoadbalancerIPMode(t *testing.T) { }}, expectedIPMode: []*v1.LoadBalancerIPMode{&modeVIP}, }, { - name: "Both IP and IPMode are set with LoadbalancerIPMode enabled", - ipModeEnabled: true, + name: "Both IP and IPMode are set", svc: &v1.Service{ Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, Status: v1.ServiceStatus{ @@ -2496,10 +2469,6 @@ func TestSetDefaultServiceLoadbalancerIPMode(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - if !tc.ipModeEnabled { - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) - } - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled) obj := roundTrip(t, runtime.Object(tc.svc)) svc := obj.(*v1.Service) for i, s := range svc.Status.LoadBalancer.Ingress { @@ -3395,3 +3364,13 @@ func TestSetDefaults_PodLogOptions(t *testing.T) { }) } } + +func setAllFeatures(t *testing.T, featuresEnabled bool) { + features := featuregatetesting.FeatureOverrides{} + for feature, featureSpec := range utilfeature.DefaultFeatureGate.DeepCopy().GetAll() { + if !featureSpec.LockToDefault { + features[feature] = featuresEnabled + } + } + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, features) +} diff --git a/pkg/apis/core/v1/helper/helpers.go b/pkg/apis/core/v1/helper/helpers.go index 96accbd3f699f..87fc484c8eda7 100644 --- a/pkg/apis/core/v1/helper/helpers.go +++ b/pkg/apis/core/v1/helper/helpers.go @@ -18,6 +18,7 @@ package helper import ( "fmt" + "k8s.io/klog/v2" "strings" v1 "k8s.io/api/core/v1" @@ -25,7 +26,9 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/util/validation" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/apis/core/helper" + "k8s.io/kubernetes/pkg/features" ) // IsExtendedResourceName returns true if: @@ -287,18 +290,19 @@ func AddOrUpdateTolerationInPodSpec(spec *v1.PodSpec, toleration *v1.Toleration) } // GetMatchingTolerations returns true and list of Tolerations matching all Taints if all are tolerated, or false otherwise. -func GetMatchingTolerations(taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) { +func GetMatchingTolerations(logger klog.Logger, taints []v1.Taint, tolerations []v1.Toleration) (bool, []v1.Toleration) { if len(taints) == 0 { return true, []v1.Toleration{} } if len(tolerations) == 0 && len(taints) > 0 { return false, []v1.Toleration{} } + enableComparisonOperators := utilfeature.DefaultFeatureGate.Enabled(features.TaintTolerationComparisonOperators) result := []v1.Toleration{} for i := range taints { tolerated := false for j := range tolerations { - if tolerations[j].ToleratesTaint(&taints[i]) { + if tolerations[j].ToleratesTaint(logger, &taints[i], enableComparisonOperators) { result = append(result, tolerations[j]) tolerated = true break diff --git a/pkg/apis/core/v1/zz_generated.conversion.go b/pkg/apis/core/v1/zz_generated.conversion.go index 6806244953a25..3770ea1916b7b 100644 --- a/pkg/apis/core/v1/zz_generated.conversion.go +++ b/pkg/apis/core/v1/zz_generated.conversion.go @@ -2337,6 +2337,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*corev1.WorkloadReference)(nil), (*core.WorkloadReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_WorkloadReference_To_core_WorkloadReference(a.(*corev1.WorkloadReference), b.(*core.WorkloadReference), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*core.WorkloadReference)(nil), (*corev1.WorkloadReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_core_WorkloadReference_To_v1_WorkloadReference(a.(*core.WorkloadReference), b.(*corev1.WorkloadReference), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*corev1.NodeProxyOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_url_Values_To_v1_NodeProxyOptions(a.(*url.Values), b.(*corev1.NodeProxyOptions), scope) }); err != nil { @@ -5559,6 +5569,7 @@ func autoConvert_v1_NodeStatus_To_core_NodeStatus(in *corev1.NodeStatus, out *co out.Config = (*core.NodeConfigStatus)(unsafe.Pointer(in.Config)) out.RuntimeHandlers = *(*[]core.NodeRuntimeHandler)(unsafe.Pointer(&in.RuntimeHandlers)) out.Features = (*core.NodeFeatures)(unsafe.Pointer(in.Features)) + out.DeclaredFeatures = *(*[]string)(unsafe.Pointer(&in.DeclaredFeatures)) return nil } @@ -5585,6 +5596,7 @@ func autoConvert_core_NodeStatus_To_v1_NodeStatus(in *core.NodeStatus, out *core out.Config = (*corev1.NodeConfigStatus)(unsafe.Pointer(in.Config)) out.RuntimeHandlers = *(*[]corev1.NodeRuntimeHandler)(unsafe.Pointer(&in.RuntimeHandlers)) out.Features = (*corev1.NodeFeatures)(unsafe.Pointer(in.Features)) + out.DeclaredFeatures = *(*[]string)(unsafe.Pointer(&in.DeclaredFeatures)) return nil } @@ -6306,6 +6318,7 @@ func autoConvert_v1_PodCertificateProjection_To_core_PodCertificateProjection(in out.CredentialBundlePath = in.CredentialBundlePath out.KeyPath = in.KeyPath out.CertificateChainPath = in.CertificateChainPath + out.UserAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UserAnnotations)) return nil } @@ -6321,6 +6334,7 @@ func autoConvert_core_PodCertificateProjection_To_v1_PodCertificateProjection(in out.CredentialBundlePath = in.CredentialBundlePath out.KeyPath = in.KeyPath out.CertificateChainPath = in.CertificateChainPath + out.UserAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UserAnnotations)) return nil } @@ -7002,6 +7016,7 @@ func autoConvert_v1_PodSpec_To_core_PodSpec(in *corev1.PodSpec, out *core.PodSpe out.ResourceClaims = *(*[]core.PodResourceClaim)(unsafe.Pointer(&in.ResourceClaims)) out.Resources = (*core.ResourceRequirements)(unsafe.Pointer(in.Resources)) out.HostnameOverride = (*string)(unsafe.Pointer(in.HostnameOverride)) + out.WorkloadRef = (*core.WorkloadReference)(unsafe.Pointer(in.WorkloadRef)) return nil } @@ -7059,6 +7074,7 @@ func autoConvert_core_PodSpec_To_v1_PodSpec(in *core.PodSpec, out *corev1.PodSpe out.ResourceClaims = *(*[]corev1.PodResourceClaim)(unsafe.Pointer(&in.ResourceClaims)) out.Resources = (*corev1.ResourceRequirements)(unsafe.Pointer(in.Resources)) out.HostnameOverride = (*string)(unsafe.Pointer(in.HostnameOverride)) + out.WorkloadRef = (*corev1.WorkloadReference)(unsafe.Pointer(in.WorkloadRef)) return nil } @@ -7081,6 +7097,8 @@ func autoConvert_v1_PodStatus_To_core_PodStatus(in *corev1.PodStatus, out *core. out.Resize = core.PodResizeStatus(in.Resize) out.ResourceClaimStatuses = *(*[]core.PodResourceClaimStatus)(unsafe.Pointer(&in.ResourceClaimStatuses)) out.ExtendedResourceClaimStatus = (*core.PodExtendedResourceClaimStatus)(unsafe.Pointer(in.ExtendedResourceClaimStatus)) + out.AllocatedResources = *(*core.ResourceList)(unsafe.Pointer(&in.AllocatedResources)) + out.Resources = (*core.ResourceRequirements)(unsafe.Pointer(in.Resources)) return nil } @@ -7102,6 +7120,8 @@ func autoConvert_core_PodStatus_To_v1_PodStatus(in *core.PodStatus, out *corev1. out.Resize = corev1.PodResizeStatus(in.Resize) out.ResourceClaimStatuses = *(*[]corev1.PodResourceClaimStatus)(unsafe.Pointer(&in.ResourceClaimStatuses)) out.ExtendedResourceClaimStatus = (*corev1.PodExtendedResourceClaimStatus)(unsafe.Pointer(in.ExtendedResourceClaimStatus)) + out.AllocatedResources = *(*corev1.ResourceList)(unsafe.Pointer(&in.AllocatedResources)) + out.Resources = (*corev1.ResourceRequirements)(unsafe.Pointer(in.Resources)) return nil } @@ -9358,3 +9378,27 @@ func autoConvert_core_WindowsSecurityContextOptions_To_v1_WindowsSecurityContext func Convert_core_WindowsSecurityContextOptions_To_v1_WindowsSecurityContextOptions(in *core.WindowsSecurityContextOptions, out *corev1.WindowsSecurityContextOptions, s conversion.Scope) error { return autoConvert_core_WindowsSecurityContextOptions_To_v1_WindowsSecurityContextOptions(in, out, s) } + +func autoConvert_v1_WorkloadReference_To_core_WorkloadReference(in *corev1.WorkloadReference, out *core.WorkloadReference, s conversion.Scope) error { + out.Name = in.Name + out.PodGroup = in.PodGroup + out.PodGroupReplicaKey = in.PodGroupReplicaKey + return nil +} + +// Convert_v1_WorkloadReference_To_core_WorkloadReference is an autogenerated conversion function. +func Convert_v1_WorkloadReference_To_core_WorkloadReference(in *corev1.WorkloadReference, out *core.WorkloadReference, s conversion.Scope) error { + return autoConvert_v1_WorkloadReference_To_core_WorkloadReference(in, out, s) +} + +func autoConvert_core_WorkloadReference_To_v1_WorkloadReference(in *core.WorkloadReference, out *corev1.WorkloadReference, s conversion.Scope) error { + out.Name = in.Name + out.PodGroup = in.PodGroup + out.PodGroupReplicaKey = in.PodGroupReplicaKey + return nil +} + +// Convert_core_WorkloadReference_To_v1_WorkloadReference is an autogenerated conversion function. +func Convert_core_WorkloadReference_To_v1_WorkloadReference(in *core.WorkloadReference, out *corev1.WorkloadReference, s conversion.Scope) error { + return autoConvert_core_WorkloadReference_To_v1_WorkloadReference(in, out, s) +} diff --git a/pkg/apis/core/v1/zz_generated.defaults.go b/pkg/apis/core/v1/zz_generated.defaults.go index 0156add916394..988066254a226 100644 --- a/pkg/apis/core/v1/zz_generated.defaults.go +++ b/pkg/apis/core/v1/zz_generated.defaults.go @@ -546,6 +546,11 @@ func SetObjectDefaults_Pod(in *corev1.Pod) { SetDefaults_ResourceList(&a.Resources.Requests) } } + SetDefaults_ResourceList(&in.Status.AllocatedResources) + if in.Status.Resources != nil { + SetDefaults_ResourceList(&in.Status.Resources.Limits) + SetDefaults_ResourceList(&in.Status.Resources.Requests) + } } func SetObjectDefaults_PodList(in *corev1.PodList) { @@ -584,6 +589,11 @@ func SetObjectDefaults_PodStatusResult(in *corev1.PodStatusResult) { SetDefaults_ResourceList(&a.Resources.Requests) } } + SetDefaults_ResourceList(&in.Status.AllocatedResources) + if in.Status.Resources != nil { + SetDefaults_ResourceList(&in.Status.Resources.Limits) + SetDefaults_ResourceList(&in.Status.Resources.Requests) + } } func SetObjectDefaults_PodTemplate(in *corev1.PodTemplate) { diff --git a/pkg/apis/core/v1/zz_generated.validations.go b/pkg/apis/core/v1/zz_generated.validations.go index b7cb065edda70..7e39374b6b2c6 100644 --- a/pkg/apis/core/v1/zz_generated.validations.go +++ b/pkg/apis/core/v1/zz_generated.validations.go @@ -30,6 +30,7 @@ import ( operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" field "k8s.io/apimachinery/pkg/util/validation/field" ) @@ -39,6 +40,7 @@ func init() { localSchemeBuilder.Register(RegisterValidations) } // RegisterValidations adds validation functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterValidations(scheme *runtime.Scheme) error { + // type ReplicationController scheme.AddValidationFunc((*corev1.ReplicationController)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/", "/scale": @@ -46,6 +48,7 @@ func RegisterValidations(scheme *runtime.Scheme) error { } return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} }) + // type ReplicationControllerList scheme.AddValidationFunc((*corev1.ReplicationControllerList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { switch op.Request.SubresourcePath() { case "/": @@ -56,64 +59,105 @@ func RegisterValidations(scheme *runtime.Scheme) error { return nil } +// Validate_ReplicationController validates an instance of ReplicationController according +// to declarative validation rules in the API schema. func Validate_ReplicationController(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *corev1.ReplicationController) (errs field.ErrorList) { // field corev1.ReplicationController.TypeMeta has no validation - // field corev1.ReplicationController.ObjectMeta has no validation + + // field corev1.ReplicationController.ObjectMeta + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *metav1.ObjectMeta, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + func() { // cohort name + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *metav1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.OptionalValue); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *metav1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.LongName)...) + }() + return + }(fldPath.Child("metadata"), &obj.ObjectMeta, safe.Field(oldObj, func(oldObj *corev1.ReplicationController) *metav1.ObjectMeta { return &oldObj.ObjectMeta }), oldObj != nil)...) // field corev1.ReplicationController.Spec errs = append(errs, - func(fldPath *field.Path, obj, oldObj *corev1.ReplicationControllerSpec) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *corev1.ReplicationControllerSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function errs = append(errs, Validate_ReplicationControllerSpec(ctx, op, fldPath, obj, oldObj)...) return - }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *corev1.ReplicationController) *corev1.ReplicationControllerSpec { return &oldObj.Spec }))...) + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *corev1.ReplicationController) *corev1.ReplicationControllerSpec { return &oldObj.Spec }), oldObj != nil)...) // field corev1.ReplicationController.Status has no validation return errs } +// Validate_ReplicationControllerList validates an instance of ReplicationControllerList according +// to declarative validation rules in the API schema. func Validate_ReplicationControllerList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *corev1.ReplicationControllerList) (errs field.ErrorList) { // field corev1.ReplicationControllerList.TypeMeta has no validation // field corev1.ReplicationControllerList.ListMeta has no validation // field corev1.ReplicationControllerList.Items errs = append(errs, - func(fldPath *field.Path, obj, oldObj []corev1.ReplicationController) (errs field.ErrorList) { - if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj []corev1.ReplicationController, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil } + // iterate the list and call the type's validation function errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ReplicationController)...) return - }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerList) []corev1.ReplicationController { return oldObj.Items }))...) + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerList) []corev1.ReplicationController { return oldObj.Items }), oldObj != nil)...) return errs } +// Validate_ReplicationControllerSpec validates an instance of ReplicationControllerSpec according +// to declarative validation rules in the API schema. func Validate_ReplicationControllerSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *corev1.ReplicationControllerSpec) (errs field.ErrorList) { // field corev1.ReplicationControllerSpec.Replicas errs = append(errs, - func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { - if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { - return nil // no changes + func(fldPath *field.Path, obj, oldObj *int32, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil } + // call field-attached validations + earlyReturn := false // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { return // do not proceed } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return - }(fldPath.Child("replicas"), obj.Replicas, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return oldObj.Replicas }))...) + }(fldPath.Child("replicas"), obj.Replicas, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return oldObj.Replicas }), oldObj != nil)...) // field corev1.ReplicationControllerSpec.MinReadySeconds errs = append(errs, - func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + func(fldPath *field.Path, obj, oldObj *int32, oldValueCorrelated bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation - if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { - return nil // no changes + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil } + // call field-attached validations errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return - }(fldPath.Child("minReadySeconds"), &obj.MinReadySeconds, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return &oldObj.MinReadySeconds }))...) + }(fldPath.Child("minReadySeconds"), &obj.MinReadySeconds, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return &oldObj.MinReadySeconds }), oldObj != nil)...) // field corev1.ReplicationControllerSpec.Selector has no validation // field corev1.ReplicationControllerSpec.Template has no validation diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 9f1ec62259d39..fbb62132d630d 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -17,6 +17,7 @@ limitations under the License. package validation import ( + "context" "encoding/json" "fmt" "math" @@ -26,6 +27,7 @@ import ( "reflect" "regexp" "slices" + "strconv" "strings" "sync" "unicode" @@ -35,7 +37,10 @@ import ( v1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/operation" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/api/validate" + "k8s.io/apimachinery/pkg/api/validate/content" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" @@ -77,6 +82,10 @@ var fileModeErrorMsg = "must be a number between 0 and 0777 (octal), both inclus // BannedOwners is a black list of object that are not allowed to be owners. var BannedOwners = apimachineryvalidation.BannedOwners +// nodeDeclaredFeatureRegexp defines the allowed format for feature names. +// The first segment must be in UpperCamelCase. Subsequent segments (separated by '/') +// can be in either UpperCamelCase or lowerCamelCase. +var nodeDeclaredFeatureRegexp = regexp.MustCompile(`^[A-Z][a-zA-Z0-9]*(\/[a-zA-Z][a-zA-Z0-9]*)*$`) var iscsiInitiatorIqnRegex = regexp.MustCompile(`iqn\.\d{4}-\d{2}\.([[:alnum:]-.]+)(:[^,;*&$|\s]+)$`) var iscsiInitiatorEuiRegex = regexp.MustCompile(`^eui.[[:alnum:]]{16}$`) var iscsiInitiatorNaaRegex = regexp.MustCompile(`^naa.[[:alnum:]]{32}$`) @@ -133,10 +142,23 @@ func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) fie return apimachineryvalidation.ValidateAnnotations(annotations, fldPath) } +// ValidateUserAnnotations validates that the UserAnnotations are correctly defined. +func ValidateUserAnnotations(userAnnotations map[string]string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + for k := range userAnnotations { + // The case doesn't matter, so convert to lowercase before checking. + allErrs = append(allErrs, validation.IsDomainPrefixedKey(fldPath, strings.ToLower(k))...) + } + if err := apimachineryvalidation.ValidateAnnotationsSize(userAnnotations); err != nil { + allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, apimachineryvalidation.TotalAnnotationSizeLimitB)) + } + return allErrs +} + func ValidateDNS1123Label(value string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} for _, msg := range validation.IsDNS1123Label(value) { - allErrs = append(allErrs, field.Invalid(fldPath, value, msg).WithOrigin("format=dns-label")) + allErrs = append(allErrs, field.Invalid(fldPath, value, msg).WithOrigin("format=k8s-short-name")) } return allErrs } @@ -154,7 +176,7 @@ func ValidateQualifiedName(value string, fldPath *field.Path) field.ErrorList { func ValidateDNS1123SubdomainWithUnderScore(value string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} for _, msg := range validation.IsDNS1123SubdomainWithUnderscore(value) { - allErrs = append(allErrs, field.Invalid(fldPath, value, msg)) + allErrs = append(allErrs, field.Invalid(fldPath, value, msg)).WithOrigin("format=k8s-dns-subdomain-with-underscore") } return allErrs } @@ -163,7 +185,7 @@ func ValidateDNS1123SubdomainWithUnderScore(value string, fldPath *field.Path) f func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} for _, msg := range validation.IsDNS1123Subdomain(value) { - allErrs = append(allErrs, field.Invalid(fldPath, value, msg)) + allErrs = append(allErrs, field.Invalid(fldPath, value, msg)).WithOrigin("format=k8s-long-name") } return allErrs } @@ -178,7 +200,7 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, spec *core.Po } if annotations[core.TolerationsAnnotationKey] != "" { - allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...) + allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath, opts)...) } if !opts.AllowInvalidPodDeletionCost { @@ -194,7 +216,7 @@ func ValidatePodSpecificAnnotations(annotations map[string]string, spec *core.Po } // ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data -func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList { +func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { allErrs := field.ErrorList{} tolerations, err := helper.GetTolerationsFromPodAnnotations(annotations) @@ -204,7 +226,7 @@ func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath } if len(tolerations) > 0 { - allErrs = append(allErrs, ValidateTolerations(tolerations, fldPath.Child(core.TolerationsAnnotationKey))...) + allErrs = append(allErrs, ValidateTolerations(tolerations, fldPath.Child(core.TolerationsAnnotationKey), opts)...) } return allErrs @@ -253,17 +275,15 @@ func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath // value that were not valid. Otherwise this returns an empty list or nil. type ValidateNameFunc apimachineryvalidation.ValidateNameFunc +// ValidateNameFuncWithErrors validates that the provided name is valid for a +// given resource type. +type ValidateNameFuncWithErrors = apimachineryvalidation.ValidateNameFuncWithErrors + // ValidatePodName can be used to check whether the given pod name is valid. // Prefix indicates this name will be used as part of generation, in which case // trailing dashes are allowed. var ValidatePodName = apimachineryvalidation.NameIsDNSSubdomain -// ValidateReplicationControllerName can be used to check whether the given replication -// controller name is valid. -// Prefix indicates this name will be used as part of generation, in which case -// trailing dashes are allowed. -var ValidateReplicationControllerName = apimachineryvalidation.NameIsDNSSubdomain - // ValidateServiceName can be used to check whether the given service name is valid. // Prefix indicates this name will be used as part of generation, in which case // trailing dashes are allowed. @@ -322,6 +342,18 @@ var ValidateResourceClaimName = apimachineryvalidation.NameIsDNSSubdomain // name for a ResourceClaimTemplate is valid. var ValidateResourceClaimTemplateName = apimachineryvalidation.NameIsDNSSubdomain +// ValidateWorkloadName can be used to check whether the given +// name for a Workload is valid. +var ValidateWorkloadName = apimachineryvalidation.NameIsDNSSubdomain + +// ValidatePodGroupName can be used to check whether the given +// name for a PodGroup is valid. +var ValidatePodGroupName = apimachineryvalidation.NameIsDNSLabel + +// ValidatePodGroupReplicaKey can be used to check whether the given +// PodGroupReplicaKey is valid. +var ValidatePodGroupReplicaKey = apimachineryvalidation.NameIsDNSLabel + // ValidateRuntimeClassName can be used to check whether the given RuntimeClass name is valid. // Prefix indicates this name will be used as part of generation, in which case // trailing dashes are allowed. @@ -384,9 +416,19 @@ func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string return allErrs } +// ValidateObjectMetaWithOpts validates an object's metadata on creation. It expects that name generation has already +// been performed. +func ValidateObjectMetaWithOpts(meta *metav1.ObjectMeta, isNamespaced bool, nameFn ValidateNameFuncWithErrors, fldPath *field.Path) field.ErrorList { + allErrs := apimachineryvalidation.ValidateObjectMetaWithOpts(meta, isNamespaced, nameFn, fldPath) + // run additional checks for the finalizer name + for i := range meta.Finalizers { + allErrs = append(allErrs, validateKubeFinalizerName(string(meta.Finalizers[i]), fldPath.Child("finalizers").Index(i))...) + } + return allErrs +} + // ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already // been performed. -// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before. // TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate. func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList { allErrs := apimachineryvalidation.ValidateObjectMeta(meta, requiresNamespace, apimachineryvalidation.ValidateNameFunc(nameFn), fldPath) @@ -1255,6 +1297,11 @@ func validateProjectionSources(projection *core.ProjectedVolumeSource, projectio allErrs = append(allErrs, ValidateSignerName(projPath.Child("signerName"), source.PodCertificate.SignerName)...) + if source.PodCertificate.UserAnnotations != nil { + userAnnotationsErrors := ValidateUserAnnotations(source.PodCertificate.UserAnnotations, projPath.Child("userAnnotations")) + allErrs = append(allErrs, userAnnotationsErrors...) + } + switch source.PodCertificate.KeyType { case "RSA3072", "RSA4096", "ECDSAP256", "ECDSAP384", "ECDSAP521", "ED25519": // ok @@ -1753,19 +1800,46 @@ func validatePVSecretReference(secretRef *core.SecretReference, fldPath *field.P return allErrs } -func ValidateCSIDriverName(driverName string, fldPath *field.Path) field.ErrorList { +// ValidateCSIDriverNameOption is an option for ValidateCSIDriverName. +// These options are for marking if a validation error message is covered +// by declarative validation +type ValidateCSIDriverNameOption int + +const ( + // The required check is covered by declarative validation + RequiredCovered ValidateCSIDriverNameOption = iota + // The list size check is covered by declarative validation. + SizeCovered + // The format check is covered by declarative validation. + FormatCovered +) + +func ValidateCSIDriverName(driverName string, fldPath *field.Path, opts ...ValidateCSIDriverNameOption) field.ErrorList { allErrs := field.ErrorList{} if len(driverName) == 0 { - allErrs = append(allErrs, field.Required(fldPath, "")) + err := field.Required(fldPath, "") + if slices.Contains(opts, RequiredCovered) { + err = err.MarkCoveredByDeclarative() + } + allErrs = append(allErrs, err) + return allErrs } if len(driverName) > 63 { - allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, 63)) + err := field.TooLong(fldPath, "" /*unused*/, 63) + if slices.Contains(opts, SizeCovered) { + err = err.MarkCoveredByDeclarative() + } + allErrs = append(allErrs, err) } for _, msg := range validation.IsDNS1123Subdomain(strings.ToLower(driverName)) { - allErrs = append(allErrs, field.Invalid(fldPath, driverName, msg)) + err := field.Invalid(fldPath, driverName, msg) + if slices.Contains(opts, FormatCovered) { + err = err.WithOrigin("format=k8s-long-name-caseless").MarkCoveredByDeclarative() + } + allErrs = append(allErrs, err) } return allErrs @@ -2212,7 +2286,8 @@ func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume, opts Pe allErrs = append(allErrs, ValidateImmutableField(newPv.Spec.VolumeMode, oldPv.Spec.VolumeMode, field.NewPath("volumeMode"))...) // Allow setting NodeAffinity if oldPv NodeAffinity was not set - if oldPv.Spec.NodeAffinity != nil { + if !utilfeature.DefaultFeatureGate.Enabled(features.MutablePVNodeAffinity) && + oldPv.Spec.NodeAffinity != nil { allErrs = append(allErrs, validatePvNodeAffinity(newPv.Spec.NodeAffinity, oldPv.Spec.NodeAffinity, field.NewPath("nodeAffinity"))...) } @@ -3243,17 +3318,21 @@ func validateInitContainerRestartPolicy(restartPolicy *core.ContainerRestartPoli var allErrors field.ErrorList if restartPolicy == nil { + if len(restartRules) > 0 { + allErrors = append(allErrors, field.Required(fldPath.Child("restartPolicy"), "must specify restartPolicy when restart rules are used")) + } return allErrors } if opts.AllowContainerRestartPolicyRules { switch *restartPolicy { case core.ContainerRestartPolicyAlways: - // Sidecar containers should not have restart policy rules - if len(restartRules) > 0 { + if opts.AllowRestartAllContainers { + allErrors = append(allErrors, validateContainerRestartPolicy(restartPolicy, restartRules, fldPath, opts)...) + } else if len(restartRules) > 0 { allErrors = append(allErrors, field.Forbidden(fldPath.Child("restartPolicyRules"), "restartPolicyRules are not allowed for init containers with restart policy Always")) } default: - allErrors = append(allErrors, validateContainerRestartPolicy(restartPolicy, restartRules, fldPath)...) + allErrors = append(allErrors, validateContainerRestartPolicy(restartPolicy, restartRules, fldPath, opts)...) } } else { switch *restartPolicy { @@ -3591,9 +3670,17 @@ var supportedContainerRestartPolicyOperators = sets.New( core.ContainerRestartRuleOnExitCodesOpNotIn, ) +// Supported actions depend on whether corresponding feature gates are enabled. +var ( + // Without AllowRestartAllContainers + supportedContainerRestartRuleActions = sets.New(core.ContainerRestartRuleActionRestart) + // With AllowRestartAllContainers + supportedContainerRestartRuleActionsWithRestartAllContainers = sets.New(core.ContainerRestartRuleActionRestart, core.ContainerRestartRuleActionRestartAllContainers) +) + // validateContainerRestartPolicy checks the container-level restartPolicy and restartPolicyRules are valid for -// regular containers and non-sidecar init containers. -func validateContainerRestartPolicy(policy *core.ContainerRestartPolicy, rules []core.ContainerRestartRule, fldPath *field.Path) field.ErrorList { +// regular containers, init containers, and sidecar containers. +func validateContainerRestartPolicy(policy *core.ContainerRestartPolicy, rules []core.ContainerRestartRule, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { var allErrs field.ErrorList restartPolicyFld := fldPath.Child("restartPolicy") if policy == nil { @@ -3611,10 +3698,12 @@ func validateContainerRestartPolicy(policy *core.ContainerRestartPolicy, rules [ } for i, rule := range rules { policyRulesFld := fldPath.Child("restartPolicyRules").Index(i) - - if rule.Action != core.ContainerRestartRuleActionRestart { - validActions := []core.ContainerRestartRuleAction{core.ContainerRestartRuleActionRestart} - allErrs = append(allErrs, field.NotSupported(policyRulesFld.Child("action"), rule.Action, validActions)) + allowedActions := supportedContainerRestartRuleActions + if opts.AllowRestartAllContainers { + allowedActions = supportedContainerRestartRuleActionsWithRestartAllContainers + } + if !allowedActions.Has(rule.Action) { + allErrs = append(allErrs, field.NotSupported(policyRulesFld.Child("action"), rule.Action, sets.List(supportedContainerRestartRuleActions))) } if rule.ExitCodes != nil { @@ -3733,8 +3822,8 @@ func validateInitContainers(containers []core.Container, os *core.PodOS, regular restartAlways := false // Apply the validation specific to init containers + allErrs = append(allErrs, validateInitContainerRestartPolicy(ctr.RestartPolicy, ctr.RestartPolicyRules, idxPath, opts)...) if ctr.RestartPolicy != nil { - allErrs = append(allErrs, validateInitContainerRestartPolicy(ctr.RestartPolicy, ctr.RestartPolicyRules, idxPath, opts)...) restartAlways = *ctr.RestartPolicy == core.ContainerRestartPolicyAlways } @@ -3842,9 +3931,12 @@ func validateHostUsers(spec *core.PodSpec, fldPath *field.Path, opts PodValidati // know of any good use case and we can always enable them later. // Note we already validated above spec.SecurityContext is not nil. - if spec.SecurityContext.HostNetwork { - allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostNetwork"), "when `hostUsers` is false")) + if !opts.AllowUserNamespacesHostNetworkSupport { + if spec.SecurityContext.HostNetwork { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostNetwork"), "when `hostUsers` is false")) + } } + if spec.SecurityContext.HostPID { allErrs = append(allErrs, field.Forbidden(fldPath.Child("HostPID"), "when `hostUsers` is false")) } @@ -3965,7 +4057,7 @@ func validateContainers(containers []core.Container, os *core.PodOS, volumes map allErrs = append(allErrs, validateStartupProbe(ctr.StartupProbe, gracePeriod, path.Child("startupProbe"), opts)...) if opts.AllowContainerRestartPolicyRules { - allErrs = append(allErrs, validateContainerRestartPolicy(ctr.RestartPolicy, ctr.RestartPolicyRules, path)...) + allErrs = append(allErrs, validateContainerRestartPolicy(ctr.RestartPolicy, ctr.RestartPolicyRules, path, opts)...) } else if ctr.RestartPolicy != nil { allErrs = append(allErrs, field.Forbidden(path.Child("restartPolicy"), "may not be set for non-init containers")) } @@ -4208,7 +4300,7 @@ func validateTaintEffect(effect *core.TaintEffect, allowEmpty bool, fldPath *fie } // validateOnlyAddedTolerations validates updated pod tolerations. -func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldTolerations []core.Toleration, fldPath *field.Path) field.ErrorList { +func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldTolerations []core.Toleration, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { allErrs := field.ErrorList{} for _, old := range oldTolerations { found := false @@ -4227,7 +4319,7 @@ func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldToleratio } } - allErrs = append(allErrs, ValidateTolerations(newTolerations, fldPath)...) + allErrs = append(allErrs, ValidateTolerations(newTolerations, fldPath, opts)...) return allErrs } @@ -4265,7 +4357,7 @@ func ValidateHostAliases(hostAliases []core.HostAlias, fldPath *field.Path) fiel } // ValidateTolerations tests if given tolerations have valid data. -func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) field.ErrorList { +func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path, opts PodValidationOptions) field.ErrorList { allErrors := field.ErrorList{} for i, toleration := range tolerations { idxPath := fldPath.Index(i) @@ -4296,6 +4388,23 @@ func ValidateTolerations(tolerations []core.Toleration, fldPath *field.Path) fie if len(toleration.Value) > 0 { allErrors = append(allErrors, field.Invalid(idxPath.Child("operator"), toleration.Value, "value must be empty when `operator` is 'Exists'")) } + case core.TolerationOpLt, core.TolerationOpGt: + // Numeric comparison operators require validation option + if !opts.AllowTaintTolerationComparisonOperators { + validValues := []core.TolerationOperator{core.TolerationOpEqual, core.TolerationOpExists, core.TolerationOpLt, core.TolerationOpGt} + allErrors = append(allErrors, field.NotSupported(idxPath.Child("operator"), toleration.Operator, validValues)) + break + } + + // validate value is decimal integer + for _, msg := range content.IsDecimalInteger(toleration.Value) { + allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), toleration.Value, msg)) + } + + // validate value is within int64 range + if _, err := strconv.ParseInt(toleration.Value, 10, 64); err != nil { + allErrors = append(allErrors, field.Invalid(idxPath.Child("value"), toleration.Value, err.Error())) + } default: validValues := []core.TolerationOperator{core.TolerationOpEqual, core.TolerationOpExists} allErrors = append(allErrors, field.NotSupported(idxPath.Child("operator"), toleration.Operator, validValues)) @@ -4357,6 +4466,9 @@ type PodValidationOptions struct { AllowOnlyRecursiveSELinuxChangePolicy bool // Indicates whether PodLevelResources feature is enabled or disabled. PodLevelResourcesEnabled bool + // Indicates whether InPlacePodLevelResourcesVerticalScaling feature is enabled + // or disabled. + InPlacePodLevelResourcesVerticalScalingEnabled bool // Allow sidecar containers resize policy for backward compatibility AllowSidecarResizePolicy bool // Allow invalid label-value in RequiredNodeSelector @@ -4375,6 +4487,12 @@ type PodValidationOptions struct { AllowContainerRestartPolicyRules bool // Allow user namespaces with volume devices, even though they will not function properly (should only be tolerated in updates of objects which already have this invalid configuration). AllowUserNamespacesWithVolumeDevices bool + // Allow taint toleration comparison operators (Lt, Gt) + AllowTaintTolerationComparisonOperators bool + // Allow hostNetwork pods to use user namespaces + AllowUserNamespacesHostNetworkSupport bool + // Allow containers to have restart policy rule with RestartAllContainers action, applicable for init, sidecar, and regular containers. + AllowRestartAllContainers bool } // validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set, @@ -4570,7 +4688,7 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi } if len(spec.Tolerations) > 0 { - allErrs = append(allErrs, ValidateTolerations(spec.Tolerations, fldPath.Child("tolerations"))...) + allErrs = append(allErrs, ValidateTolerations(spec.Tolerations, fldPath.Child("tolerations"), opts)...) } if len(spec.HostAliases) > 0 { @@ -4607,6 +4725,10 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi } } + if spec.WorkloadRef != nil { + allErrs = append(allErrs, validateWorkloadReference(spec.WorkloadRef, fldPath.Child("workloadRef"))...) + } + allErrs = append(allErrs, validateFileKeyRefVolumes(spec, fldPath)...) return allErrs } @@ -4855,7 +4977,7 @@ func ValidateNodeSelectorRequirement(rq core.NodeSelectorRequirement, allowInval path := fldPath.Child("values") for valueIndex, value := range rq.Values { for _, msg := range validation.IsValidLabelValue(value) { - allErrs = append(allErrs, field.Invalid(path.Index(valueIndex), value, msg)) + allErrs = append(allErrs, field.Invalid(path.Index(valueIndex), value, msg)).WithOrigin("format=k8s-label-value") } } } @@ -5616,7 +5738,7 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel } // Allow only additions to tolerations updates. - allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...) + allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"), opts)...) // Allow only deletions to schedulingGates updates. allErrs = append(allErrs, validateOnlyDeletedSchedulingGates(newPod.Spec.SchedulingGates, oldPod.Spec.SchedulingGates, specPath.Child("schedulingGates"))...) @@ -5716,14 +5838,24 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel } // ValidateContainerStateTransition test to if any illegal container state transitions are being attempted -func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldPath *field.Path, podSpec core.PodSpec) field.ErrorList { +func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldPath *field.Path, podSpec core.PodSpec, opts PodValidationOptions) field.ErrorList { allErrs := field.ErrorList{} - if utilfeature.DefaultFeatureGate.Enabled(features.ContainerRestartRules) { + if opts.AllowContainerRestartPolicyRules { + // Convert the *core.PodSpec to *v1.PodSpec to satisfy the call to AllContainersCouldRestart and + // ContainerShouldRestart methods in the subsequent lines, which requires a *v1.PodSpec object. v1PodSpec := &v1.PodSpec{} err := corev1.Convert_core_PodSpec_To_v1_PodSpec(&podSpec, v1PodSpec, nil) if err != nil { allErrs = append(allErrs, field.InternalError(fldPath, fmt.Errorf("invalid %q: %v", fldPath, err.Error()))) } + if opts.AllowRestartAllContainers { + // If any container has a restart rule action of RestartAllContainer, it means all container + // could be restarted, no matter their own restart policy. Therefore, all container could transition + // from terminated to non-terminated states. + if podutil.AllContainersCouldRestart(v1PodSpec) { + return allErrs + } + } for i, oldStatus := range oldStatuses { // Skip any container that is not terminated if oldStatus.State.Terminated == nil { @@ -5771,8 +5903,47 @@ func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerS } // ValidateInitContainerStateTransition test to if any illegal init container state transitions are being attempted -func ValidateInitContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldpath *field.Path, podSpec *core.PodSpec) field.ErrorList { +func ValidateInitContainerStateTransition(newStatuses, oldStatuses []core.ContainerStatus, fldpath *field.Path, podSpec core.PodSpec, opts PodValidationOptions) field.ErrorList { allErrs := field.ErrorList{} + if opts.AllowContainerRestartPolicyRules { + // Convert the *core.PodSpec to *v1.PodSpec to satisfy the call to AllContainersCouldRestart and + // ContainerShouldRestart methods in the subsequent lines, which requires a *v1.PodSpec object. + v1PodSpec := &v1.PodSpec{} + err := corev1.Convert_core_PodSpec_To_v1_PodSpec(&podSpec, v1PodSpec, nil) + if err != nil { + allErrs = append(allErrs, field.InternalError(fldpath, fmt.Errorf("invalid %q: %v", fldpath, err.Error()))) + } + if opts.AllowRestartAllContainers { + // If any container has a restart rule action of RestartAllContainer, it means all container + // could be restarted, no matter their own restart policy. Therefore, all container could transition + // from terminated to non-terminated states. + if podutil.AllContainersCouldRestart(v1PodSpec) { + return allErrs + } + } + for i, oldStatus := range oldStatuses { + // Skip any container that is not terminated + if oldStatus.State.Terminated == nil { + continue + } + for _, newStatus := range newStatuses { + if newStatus.Name == oldStatus.Name && newStatus.State.Terminated == nil { + allowed := false + for _, c := range v1PodSpec.InitContainers { + if c.Name == oldStatus.Name { + allowed = podutil.ContainerShouldRestart(c, *v1PodSpec, oldStatus.State.Terminated.ExitCode) + break + } + } + if !allowed { + allErrs = append(allErrs, field.Forbidden(fldpath.Index(i).Child("state"), "may not be transitioned to non-terminated state")) + } + break + } + } + } + return allErrs + } // If we should always restart, containers are allowed to leave the terminated state if podSpec.RestartPolicy == core.RestartPolicyAlways { return allErrs @@ -5869,8 +6040,8 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions // // If pod should not restart, make sure the status update does not transition // any terminated containers to a non-terminated state. - allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec)...) - allErrs = append(allErrs, ValidateInitContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), &oldPod.Spec)...) + allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec, opts)...) + allErrs = append(allErrs, ValidateInitContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), oldPod.Spec, opts)...) allErrs = append(allErrs, ValidateEphemeralContainerStateTransition(newPod.Status.EphemeralContainerStatuses, oldPod.Status.EphemeralContainerStatuses, fldPath.Child("ephemeralContainerStatuses"))...) allErrs = append(allErrs, validatePodResourceClaimStatuses(newPod.Status.ResourceClaimStatuses, newPod.Spec.ResourceClaims, fldPath.Child("resourceClaimStatuses"))...) allErrs = append(allErrs, validatePodExtendedResourceClaimStatus(newPod.Status.ExtendedResourceClaimStatus, &newPod.Spec, fldPath.Child("extendedResourceClaimStatus"))...) @@ -5964,6 +6135,7 @@ func validatePodExtendedResourceClaimStatus(status *core.PodExtendedResourceClai type key struct { container string resource string + request string } seen := map[key]struct{}{} for i, rm := range status.RequestMappings { @@ -5977,10 +6149,11 @@ func validatePodExtendedResourceClaimStatus(status *core.PodExtendedResourceClai allErrs = append(allErrs, field.Invalid(idxPath.Child("containerName"), rm.ContainerName, "must match the name of an entry in spec.initContainers.name or spec.containers.name")) } allErrs = append(allErrs, ValidateDNS1123Label(rm.RequestName, fldPath.Child("requestName"))...) - k := key{container: rm.ContainerName, resource: rm.ResourceName} + k := key{container: rm.ContainerName, resource: rm.ResourceName, request: rm.RequestName} if _, ok := seen[k]; ok { allErrs = append(allErrs, field.Duplicate(idxPath.Child("containerName"), rm.ContainerName)) allErrs = append(allErrs, field.Duplicate(idxPath.Child("resourceName"), rm.ResourceName)) + allErrs = append(allErrs, field.Duplicate(idxPath.Child("requestName"), rm.RequestName)) } seen[k] = struct{}{} } @@ -6038,7 +6211,7 @@ func ValidatePodEphemeralContainersUpdate(newPod, oldPod *core.Pod, opts PodVali return allErrs } -// ValidatePodResize tests that a user update to pod container resources is valid. +// ValidatePodResize tests that an update to pod container resources is valid. // newPod and oldPod must only differ in their Containers[*].Resources and // Containers[*].ResizePolicy field. func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList { @@ -6047,16 +6220,6 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath) allErrs = append(allErrs, validatePodMetadataAndSpec(newPod, opts)...) - // pods with pod-level resources cannot be resized - isPodLevelResourcesSet := func(pod *core.Pod) bool { - return pod.Spec.Resources != nil && - (len(pod.Spec.Resources.Requests)+len(pod.Spec.Resources.Limits) > 0) - } - - if isPodLevelResourcesSet(oldPod) || isPodLevelResourcesSet(newPod) { - return field.ErrorList{field.Forbidden(field.NewPath(""), "pods with pod-level resources cannot be resized")} - } - // static pods cannot be resized. if _, ok := oldPod.Annotations[core.MirrorPodAnnotationKey]; ok { return field.ErrorList{field.Forbidden(field.NewPath(""), "static pods cannot be resized")} @@ -6067,9 +6230,6 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel return field.ErrorList{field.Forbidden(field.NewPath(""), "windows pods cannot be resized")} } - // Part 2: Validate that the changes between oldPod.Spec.Containers[].Resources and - // newPod.Spec.Containers[].Resources are allowed. Also validate that the changes between oldPod.Spec.InitContainers[].Resources and - // newPod.Spec.InitContainers[].Resources are allowed. specPath := field.NewPath("spec") if qos.GetPodQOS(oldPod) != qos.ComputePodQOS(newPod) { allErrs = append(allErrs, field.Invalid(specPath, newPod.Status.QOSClass, "Pod QOS Class may not change as a result of resizing")) @@ -6079,6 +6239,34 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel allErrs = append(allErrs, field.Forbidden(specPath, "Pod running on node without support for resize")) } + // Part 2: Validate that changes between pod-level resources in oldPod.Spec.Resources and + // newPod.Spec.Resources are allowed. + + isPodLevelResourcesSet := func(pod *core.Pod) bool { + return pod.Spec.Resources != nil && + (len(pod.Spec.Resources.Requests)+len(pod.Spec.Resources.Limits) > 0) + } + + newPodSpecCopy := *newPod.Spec.DeepCopy() + + if isPodLevelResourcesSet(oldPod) || isPodLevelResourcesSet(newPod) { + // pods with pod-level resources cannot be resized without + // InPlacePodLevelResourcesVerticalScaling feature gate being enabled. + if !opts.InPlacePodLevelResourcesVerticalScalingEnabled { + allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "resize of pods with pod-level resources is forbidden when InPlacePodLevelResourcesVerticalScalingEnabled feature gate is disabled")) + return allErrs + } + + // newPodSpecCopy is passed by reference to allow + // newPodSpecCopy.Resources to be mutated which is required for an + // intermediate validation step. + allErrs = append(allErrs, validatePodLevelResourcesResize(newPod, oldPod, &newPodSpecCopy, specPath, opts)...) + } + + // Part 3: Validate that the changes between oldPod.Spec.Containers[].Resources and + // newPod.Spec.Containers[].Resources are allowed. Also validate that the changes between oldPod.Spec.InitContainers[].Resources and + // newPod.Spec.InitContainers[].Resources are allowed. + // The rest of the validation assumes that the containers are in the same order, // so we proceed only if that assumption is true. containerOrderErrs := validatePodResizeContainerOrdering(newPod, oldPod, specPath) @@ -6109,9 +6297,8 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel } // Ensure that only CPU and memory resources are mutable for regular containers. - originalCPUMemPodSpec := *newPod.Spec.DeepCopy() var newContainers []core.Container - for ix, container := range originalCPUMemPodSpec.Containers { + for ix, container := range newPodSpecCopy.Containers { dropCPUMemoryResourcesFromContainer(&container, &oldPod.Spec.Containers[ix]) if !apiequality.Semantic.DeepEqual(container, oldPod.Spec.Containers[ix]) { // This likely means that the user has made changes to resources other than CPU and memory for regular container. @@ -6120,13 +6307,13 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel } newContainers = append(newContainers, container) } - originalCPUMemPodSpec.Containers = newContainers + newPodSpecCopy.Containers = newContainers // Ensure that only CPU and memory resources are mutable for restartable init containers. // Also ensure that resources are immutable for non-restartable init containers. var newInitContainers []core.Container if utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { - for ix, container := range originalCPUMemPodSpec.InitContainers { + for ix, container := range newPodSpecCopy.InitContainers { if isRestartableInitContainer(&container) { // restartable init container dropCPUMemoryResourcesFromContainer(&container, &oldPod.Spec.InitContainers[ix]) if !apiequality.Semantic.DeepEqual(container, oldPod.Spec.InitContainers[ix]) { @@ -6141,14 +6328,14 @@ func ValidatePodResize(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel } newInitContainers = append(newInitContainers, container) } - originalCPUMemPodSpec.InitContainers = newInitContainers + newPodSpecCopy.InitContainers = newInitContainers } if len(allErrs) > 0 { return allErrs } - if !apiequality.Semantic.DeepEqual(originalCPUMemPodSpec, oldPod.Spec) { + if !apiequality.Semantic.DeepEqual(newPodSpecCopy, oldPod.Spec) { // This likely means that the user has made changes to resources other than CPU and Memory. errs := field.Forbidden(specPath, "only cpu and memory resources are mutable") allErrs = append(allErrs, errs) @@ -6181,33 +6368,113 @@ func validatePodResizeContainerOrdering(newPod, oldPod *core.Pod, specPath *fiel return allErrs } -// dropCPUMemoryResourcesFromContainer deletes the cpu and memory resources from the container, and copies them from the old pod container resources if present. -func dropCPUMemoryResourcesFromContainer(container *core.Container, oldPodSpecContainer *core.Container) { - dropCPUMemoryUpdates := func(resourceList, oldResourceList core.ResourceList) core.ResourceList { - if oldResourceList == nil { - return nil - } - var mungedResourceList core.ResourceList - if resourceList == nil { - mungedResourceList = make(core.ResourceList) - } else { - mungedResourceList = resourceList.DeepCopy() +// validatePodLevelResourcesResize validates updates to pod-level resource requests/limits +// for in-place resizing, and is intentionally designed to mutate the +// podSpecToMutate, which is a deep copy of newPod.Spec +// +// The function performs a masking operation on its .Resources field: +// +// 1. Masking/Equalization: It modifies .Resources by dropping CPU/Memory +// resource values in podSpecToMutate, and then copying specific CPU/Memory resource +// values to match oldPod.Spec.Resources. +// 2. Integrity Check: The resulting mutated spec is used to perform an equality +// comparison (DeepEqual) against oldPod.Spec. If the specs match after masking, +// it guarantees that no changes were made other than the allowed resource modifications +// needed for the resize. +func validatePodLevelResourcesResize(newPod, oldPod *core.Pod, podSpecToMutate *core.PodSpec, specPath *field.Path, opts PodValidationOptions) field.ErrorList { + var allErrs field.ErrorList + + if oldPod.Spec.Resources != nil && newPod.Spec.Resources == nil { + return append(allErrs, field.Forbidden(specPath.Child("resources"), "pod-level resources cannot be removed")) + } + + if oldPod.Spec.Resources != nil { + if resourcesRemoved(newPod.Spec.Resources.Requests, oldPod.Spec.Resources.Requests) { + allErrs = append(allErrs, field.Forbidden(specPath.Child("resources").Child("requests"), "pod-level resource requests cannot be removed")) } - delete(mungedResourceList, core.ResourceCPU) - delete(mungedResourceList, core.ResourceMemory) - if cpu, found := oldResourceList[core.ResourceCPU]; found { - mungedResourceList[core.ResourceCPU] = cpu + + if resourcesRemoved(newPod.Spec.Resources.Limits, oldPod.Spec.Resources.Limits) { + allErrs = append(allErrs, field.Forbidden(specPath.Child("resources").Child("limits"), "pod-level resource limits cannot be removed")) } - if mem, found := oldResourceList[core.ResourceMemory]; found { - mungedResourceList[core.ResourceMemory] = mem + + } + + podSpecToMutate.Resources = dropCPUMemoryResourceRequirementsUpdates(podSpecToMutate.Resources, oldPod.Spec.Resources) + + if !apiequality.Semantic.DeepEqual(podSpecToMutate.Resources, oldPod.Spec.Resources) { + // This likely means that the user has made changes to pod-level resources other + // than CPU and memory. + errs := field.Forbidden(specPath, "only cpu and memory resources are mutable at pod-level") + allErrs = append(allErrs, errs) + } + + return allErrs +} + +func dropCPUMemoryUpdates(resourceList, oldResourceList core.ResourceList) core.ResourceList { + var mungedResourceList core.ResourceList + if resourceList == nil { + if oldResourceList == nil { + return nil } - return mungedResourceList + mungedResourceList = make(core.ResourceList) + } else { + mungedResourceList = resourceList.DeepCopy() } + delete(mungedResourceList, core.ResourceCPU) + delete(mungedResourceList, core.ResourceMemory) + if cpu, found := oldResourceList[core.ResourceCPU]; found { + mungedResourceList[core.ResourceCPU] = cpu + } + if mem, found := oldResourceList[core.ResourceMemory]; found { + mungedResourceList[core.ResourceMemory] = mem + } + return mungedResourceList +} + +// dropCPUMemoryResourcesFromContainer deletes the cpu and memory resources from the +// container, and copies them from the old pod container resources if present. +// TODO(ndixita): refactor to reuse dropCPUMemoryResourceRequirementsUpdates +func dropCPUMemoryResourcesFromContainer(container *core.Container, oldPodSpecContainer *core.Container) { lim := dropCPUMemoryUpdates(container.Resources.Limits, oldPodSpecContainer.Resources.Limits) req := dropCPUMemoryUpdates(container.Resources.Requests, oldPodSpecContainer.Resources.Requests) container.Resources = core.ResourceRequirements{Limits: lim, Requests: req} } +// dropCPUMemoryResourceRequirementsUpdates deletes the cpu and memory resources +// from the `resources` field, and copies them from old Pod spec's resources field +// if present. +func dropCPUMemoryResourceRequirementsUpdates(resources *core.ResourceRequirements, oldPodResources *core.ResourceRequirements) *core.ResourceRequirements { + if resources == nil { + return resources + } + + var oldReqs, oldLims core.ResourceList + if oldPodResources != nil && oldPodResources.Requests != nil { + oldReqs = oldPodResources.Requests // +k8s:verify-mutation:reason=clone' + } + + if oldPodResources != nil && oldPodResources.Limits != nil { + oldLims = oldPodResources.Limits // +k8s:verify-mutation:reason=clone' + } + + resources.Requests = dropCPUMemoryUpdates(resources.Requests, oldReqs) + resources.Limits = dropCPUMemoryUpdates(resources.Limits, oldLims) + + // Set the entire Resources block to nil if two conditions are met: + // 1. The old PodSpec Resources lacked any Resources + // 2. The masked resources has only empty Requests/Limits + // (i.e., any prior CPU/Memory defaults have been dropped) AND no immutable Claims are set. + // This ensures that an empty Resources block (which would typically only contain + // empty CPU/Memory after masking) is safely set to nil, unless Claims are present. + if oldPodResources == nil { + if len(resources.Requests) == 0 && len(resources.Limits) == 0 && len(resources.Claims) == 0 { + resources = nil + } + } + return resources +} + // isPodResizeRequestSupported checks whether the pod is running on a node with InPlacePodVerticalScaling enabled. func isPodResizeRequestSupported(pod core.Pod) bool { // TODO: Remove this after GA+3 releases of InPlacePodVerticalScaling @@ -6700,7 +6967,10 @@ func ValidateServiceStatusUpdate(service, oldService *core.Service) field.ErrorL // ValidateReplicationController tests if required fields in the replication controller are set. func ValidateReplicationController(controller *core.ReplicationController, opts PodValidationOptions) field.ErrorList { - allErrs := ValidateObjectMeta(&controller.ObjectMeta, true, ValidateReplicationControllerName, field.NewPath("metadata")) + validateLongName := func(fldPath *field.Path, name string) field.ErrorList { + return validate.LongName(context.Background(), operation.Operation{}, fldPath, &name, nil).MarkCoveredByDeclarative() + } + allErrs := ValidateObjectMetaWithOpts(&controller.ObjectMeta, true, validateLongName, field.NewPath("metadata")) allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec, nil, field.NewPath("spec"), opts)...) return allErrs } @@ -6873,6 +7143,37 @@ func ValidateNodeSpecificAnnotations(annotations map[string]string, fldPath *fie return allErrs } +// validateNodeDeclaredFeatureName checks if a declared feature name is valid. +func validateNodeDeclaredFeatureName(featureName string) error { + if len(featureName) > validation.DNS1123SubdomainMaxLength { + return fmt.Errorf("invalid feature name %q: must be no more than %d characters", featureName, validation.DNS1123SubdomainMaxLength) + } + if !nodeDeclaredFeatureRegexp.MatchString(featureName) { + return fmt.Errorf("invalid feature name %q: must start with an UpperCamelCase segment, with subsequent segments separated by '/' (e.g., MyFeature or MyFeature/mySubFeature), and contain only alphanumeric characters and slashes", featureName) + } + return nil +} + +func validateNodeDeclaredFeatures(nodeFeatures []string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + // We do not need to check feature gate again here as we do it in dropDisabledFields() (pkg/registry/core/node/strategy.go) + featureCount := len(nodeFeatures) + for i, feature := range nodeFeatures { + if err := validateNodeDeclaredFeatureName(feature); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i), feature, err.Error())) + } + if i+1 < featureCount { + nextFeature := nodeFeatures[i+1] + if feature == nextFeature { + allErrs = append(allErrs, field.Duplicate(fldPath.Index(i+1), nextFeature)) + } else if feature > nextFeature { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i+1), nextFeature, "list must be sorted alphabetically")) + } + } + } + return allErrs +} + // ValidateNode tests if required fields in the node are set. func ValidateNode(node *core.Node) field.ErrorList { fldPath := field.NewPath("metadata") @@ -6887,6 +7188,8 @@ func ValidateNode(node *core.Node) field.ErrorList { // That said, if specified, we need to ensure they are valid. allErrs = append(allErrs, ValidateNodeResources(node)...) allErrs = append(allErrs, validateNodeSwapStatus(node.Status.NodeInfo.Swap, fldPath.Child("nodeSwapStatus"))...) + statusField := field.NewPath("status") + allErrs = append(allErrs, validateNodeDeclaredFeatures(node.Status.DeclaredFeatures, statusField.Child("declaredFeatures"))...) // validate PodCIDRS only if we need to if len(node.Spec.PodCIDRs) > 0 { @@ -6935,15 +7238,17 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList { fldPath := field.NewPath("metadata") allErrs := ValidateObjectMetaUpdate(&node.ObjectMeta, &oldNode.ObjectMeta, fldPath) allErrs = append(allErrs, ValidateNodeSpecificAnnotations(node.ObjectMeta.Annotations, fldPath.Child("annotations"))...) - // TODO: Enable the code once we have better core object.status update model. Currently, // anyone can update node status. // if !apiequality.Semantic.DeepEqual(node.Status, core.NodeStatus{}) { // allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty")) // } - allErrs = append(allErrs, ValidateNodeResources(node)...) + // TODO: dedup the validation checks in ValidateNode() and ValidateNodeUpdate() since both these functions get called during node update. + statusField := field.NewPath("status") + allErrs = append(allErrs, validateNodeDeclaredFeatures(node.Status.DeclaredFeatures, statusField.Child("declaredFeatures"))...) + // Validate no duplicate addresses in node status. addresses := make(map[core.NodeAddress]bool) for i, address := range node.Status.Addresses { @@ -8353,7 +8658,7 @@ var ( func ValidateLoadBalancerStatus(status, oldStatus *core.LoadBalancerStatus, fldPath *field.Path, spec *core.ServiceSpec) field.ErrorList { allErrs := field.ErrorList{} ingrPath := fldPath.Child("ingress") - if !utilfeature.DefaultFeatureGate.Enabled(features.AllowServiceLBStatusOnNonLB) && spec.Type != core.ServiceTypeLoadBalancer && len(status.Ingress) != 0 { + if spec.Type != core.ServiceTypeLoadBalancer && len(status.Ingress) != 0 { allErrs = append(allErrs, field.Forbidden(ingrPath, "may only be used when `spec.type` is 'LoadBalancer'")) } else { var existingIngressIPs []string @@ -8371,7 +8676,7 @@ func ValidateLoadBalancerStatus(status, oldStatus *core.LoadBalancerStatus, fldP allErrs = append(allErrs, IsValidIPForLegacyField(idxPath.Child("ip"), ingress.IP, existingIngressIPs)...) } - if utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) && ingress.IPMode == nil { + if ingress.IPMode == nil { if len(ingress.IP) > 0 { allErrs = append(allErrs, field.Required(idxPath.Child("ipMode"), "must be specified when `ip` is set")) } @@ -8385,7 +8690,7 @@ func ValidateLoadBalancerStatus(status, oldStatus *core.LoadBalancerStatus, fldP for _, msg := range validation.IsDNS1123Subdomain(ingress.Hostname) { allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg)) } - if isIP := (netutils.ParseIPSloppy(ingress.Hostname) != nil); isIP { + if isIP := netutils.ParseIPSloppy(ingress.Hostname) != nil; isIP { allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address")) } } @@ -9282,3 +9587,19 @@ func validateNodeSwapStatus(nodeSwapStatus *core.NodeSwapStatus, fldPath *field. return allErrors } + +func validateWorkloadReference(workloadRef *core.WorkloadReference, fldPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + for _, detail := range ValidateWorkloadName(workloadRef.Name, false) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), workloadRef.Name, detail)) + } + for _, detail := range ValidatePodGroupName(workloadRef.PodGroup, false) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("podGroup"), workloadRef.PodGroup, detail)) + } + if workloadRef.PodGroupReplicaKey != "" { + for _, detail := range ValidatePodGroupReplicaKey(workloadRef.PodGroupReplicaKey, false) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("podGroupReplicaKey"), workloadRef.PodGroupReplicaKey, detail)) + } + } + return allErrs +} diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index 15aed1b0a5610..97073c06c629f 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -34,6 +34,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" @@ -42,6 +43,7 @@ import ( "k8s.io/apimachinery/pkg/util/version" utilfeature "k8s.io/apiserver/pkg/util/feature" featuregatetesting "k8s.io/component-base/featuregate/testing" + ndf "k8s.io/component-helpers/nodedeclaredfeatures/features" kubeletapis "k8s.io/kubelet/pkg/apis" podtest "k8s.io/kubernetes/pkg/api/pod/testing" "k8s.io/kubernetes/pkg/apis/core" @@ -1081,6 +1083,7 @@ func pvcWithDataSource(dataSource *core.TypedLocalObjectReference) *core.Persist }, } } + func pvcWithDataSourceRef(ref *core.TypedObjectReference) *core.PersistentVolumeClaim { return &core.PersistentVolumeClaim{ Spec: core.PersistentVolumeClaimSpec{ @@ -1238,9 +1241,10 @@ func multipleVolumeNodeAffinity(terms [][]topologyPair) *core.VolumeNodeAffinity func TestValidateVolumeNodeAffinityUpdate(t *testing.T) { scenarios := map[string]struct { - isExpectedFailure bool - oldPV *core.PersistentVolume - newPV *core.PersistentVolume + mutablePVNodeAffinity bool + isExpectedFailure bool + oldPV *core.PersistentVolume + newPV *core.PersistentVolume }{ "nil-nothing-changed": { isExpectedFailure: false, @@ -1505,9 +1509,16 @@ func TestValidateVolumeNodeAffinityUpdate(t *testing.T) { oldPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity(v1.LabelInstanceType, "-1")), newPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity(v1.LabelInstanceTypeStable, "-1")), }, + "MutablePVNodeAffinity": { + mutablePVNodeAffinity: true, + isExpectedFailure: false, + oldPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "bar")), + newPV: testVolumeWithNodeAffinity(simpleVolumeNodeAffinity("foo", "baz")), + }, } for name, scenario := range scenarios { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutablePVNodeAffinity, scenario.mutablePVNodeAffinity) originalNewPV := scenario.newPV.DeepCopy() originalOldPV := scenario.oldPV.DeepCopy() opts := ValidationOptionsForPersistentVolume(scenario.newPV, scenario.oldPV) @@ -3104,8 +3115,10 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, scenario.enableRecoverFromExpansion) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeAttributesClass, scenario.enableVolumeAttributesClass) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.RecoverVolumeExpansionFailure: scenario.enableRecoverFromExpansion, + features.VolumeAttributesClass: scenario.enableVolumeAttributesClass, + }) scenario.oldClaim.ResourceVersion = "1" scenario.newClaim.ResourceVersion = "1" @@ -7100,13 +7113,7 @@ func TestValidateEnvVarValueFromFileKeyRef(t *testing.T) { }, opts: PodValidationOptions{}, expectedErrs: field.ErrorList{ - { - Type: field.ErrorTypeInvalid, - Field: field.NewPath("valueFrom.fileKeyRef.volumeName").String(), - BadValue: "INVALID_NAME!", - Detail: "a lowercase RFC 1123 label must consist of", - Origin: "format=dns-label", - }, + field.Invalid(field.NewPath("valueFrom.fileKeyRef.volumeName"), "INVALID_NAME!", "").WithOrigin("format=k8s-short-name"), }, }, { @@ -7158,13 +7165,7 @@ func TestValidateEnvVarValueFromFileKeyRef(t *testing.T) { opts: PodValidationOptions{}, expectedErrs: field.ErrorList{ field.Invalid(field.NewPath("valueFrom.fileKeyRef.key"), "bad=key", "environment variable"), - { - Type: field.ErrorTypeInvalid, - Field: field.NewPath("valueFrom.fileKeyRef.volumeName").String(), - BadValue: "!badname", - Detail: "a lowercase RFC 1123 label must consist of", - Origin: "format=dns-label", - }, + field.Invalid(field.NewPath("valueFrom.fileKeyRef.volumeName"), "!badname", "").WithOrigin("format=k8s-short-name"), field.Invalid(field.NewPath("valueFrom.fileKeyRef.path"), "../badpath", "must not contain '..'"), }, }, @@ -11203,6 +11204,27 @@ func TestValidatePod(t *testing.T) { }, }), ), + "valid PodCertificate projected volume source, user config is not nil": *podtest.MakePod("valid-podcertificate-5", + podtest.SetVolumes(core.Volume{ + Name: "projected-volume", + VolumeSource: core.VolumeSource{ + Projected: &core.ProjectedVolumeSource{ + Sources: []core.VolumeProjection{ + { + PodCertificate: &core.PodCertificateProjection{ + SignerName: "example.com/foo", + KeyType: "ED25519", + MaxExpirationSeconds: ptr.To[int32](3600), + KeyPath: "key.pem", + CertificateChainPath: "certificates.pem", + UserAnnotations: map[string]string{"test.domain/attribute": "test-value"}, + }, + }, + }, + }, + }, + }), + ), "ephemeral volume + PVC, no conflict between them": *podtest.MakePod("valid-extended", podtest.SetVolumes( core.Volume{Name: "pvc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "my-pvc"}}}, @@ -12224,6 +12246,18 @@ func TestValidatePod(t *testing.T) { podtest.SetTolerations(core.Toleration{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoSchedule", TolerationSeconds: &[]int64{20}[0]}), ), }, + "numeric operator Lt requires feature gate (gate disabled)": { + expectedError: "Unsupported value: \"Lt\"", + spec: *podtest.MakePod("123", + podtest.SetTolerations(core.Toleration{Key: "node.kubernetes.io/sla", Operator: "Lt", Value: "950", Effect: "NoSchedule"}), + ), + }, + "numeric operator Gt requires feature gate (gate disabled)": { + expectedError: "Unsupported value: \"Gt\"", + spec: *podtest.MakePod("123", + podtest.SetTolerations(core.Toleration{Key: "node.kubernetes.io/sla", Operator: "Gt", Value: "950", Effect: "NoSchedule"}), + ), + }, "must be a valid pod seccomp profile": { expectedError: "must be a valid seccomp profile", spec: *podtest.MakePod("123", @@ -12851,6 +12885,75 @@ func TestValidatePod(t *testing.T) { }), ), }, + "PodCertificate projected volume with bad user annotations, invalid key": { + expectedError: "regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]') with an optional DNS subdomain prefix", + spec: *podtest.MakePod("pod1", + podtest.SetVolumes(core.Volume{ + Name: "projected-volume", + VolumeSource: core.VolumeSource{ + Projected: &core.ProjectedVolumeSource{ + Sources: []core.VolumeProjection{ + { + PodCertificate: &core.PodCertificateProjection{ + SignerName: "example.com/foo", + KeyType: "ED25519", + KeyPath: "key.pem", + CertificateChainPath: "certificates.pem", + UserAnnotations: map[string]string{"only/one/slash": "bar"}, + }, + }, + }, + }, + }, + }), + ), + }, + "PodCertificate projected volume with bad user annotations, too long key prefix size": { + expectedError: "prefix part must be no more than 253 bytes", + spec: *podtest.MakePod("pod1", + podtest.SetVolumes(core.Volume{ + Name: "projected-volume", + VolumeSource: core.VolumeSource{ + Projected: &core.ProjectedVolumeSource{ + Sources: []core.VolumeProjection{ + { + PodCertificate: &core.PodCertificateProjection{ + SignerName: "example.com/foo", + KeyType: "ED25519", + KeyPath: "key.pem", + CertificateChainPath: "certificates.pem", + UserAnnotations: map[string]string{strings.Repeat("a", 254) + "/foo": "bar"}, + }, + }, + }, + }, + }, + }), + ), + }, + "PodCertificate projected volume with bad user annotations, too long total key/value size": { + expectedError: "may not be more than 262144 bytes", + spec: *podtest.MakePod("pod1", + podtest.SetVolumes(core.Volume{ + Name: "projected-volume", + VolumeSource: core.VolumeSource{ + Projected: &core.ProjectedVolumeSource{ + Sources: []core.VolumeProjection{ + { + PodCertificate: &core.PodCertificateProjection{ + SignerName: "example.com/foo", + KeyType: "ED25519", + KeyPath: "key.pem", + CertificateChainPath: "certificates.pem", + UserAnnotations: map[string]string{"domain/a": strings.Repeat("d", apimachineryvalidation.TotalAnnotationSizeLimitB)}, + }, + }, + }, + }, + }, + }), + ), + }, "final PVC name for ephemeral volume must be valid": { expectedError: "spec.volumes[1].name: Invalid value: \"" + longVolName + "\": PVC name \"" + longPodName + "-" + longVolName + "\": must be no more than 253 characters", spec: *podtest.MakePod(longPodName, @@ -14452,6 +14555,21 @@ func TestValidatePodUpdate(t *testing.T) { ), err: "pod updates may not change fields other than", test: "memory limit change with pod-level resources", + }, { + new: *podtest.MakePod("pod", + podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "w", + PodGroup: "pg", + }), + ), + old: *podtest.MakePod("pod", + podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "w2", + PodGroup: "pg", + }), + ), + err: "pod updates may not change fields other than", + test: "updated workloadRef", }, } @@ -15202,7 +15320,37 @@ func TestValidatePodStatusUpdate(t *testing.T) { ), old: *podtest.MakePod("foo"), err: "Duplicate value: \"ctr\"", - test: "invalid container name and extended resource name in requestMapping in ExtendedResourceClaimStatus", + test: "invalid duplicate container name,, extended resource name, and request name in requestMapping in ExtendedResourceClaimStatus", + }, { + new: *podtest.MakePod("foo", + podtest.SetContainers(podtest.MakeContainer("ctr", podtest.SetContainerResources( + podtest.MakeResourceRequirements( + map[string]string{ + string("example.com/gpu"): "1", + }, + map[string]string{ + string("example.com/gpu"): "1", + })))), + podtest.SetStatus(core.PodStatus{ + ExtendedResourceClaimStatus: &core.PodExtendedResourceClaimStatus{ + ResourceClaimName: "xyz", + RequestMappings: []core.ContainerExtendedResourceRequest{ + { + ContainerName: "ctr", + ResourceName: "example.com/gpu", + RequestName: "container-0-request-0", + }, + { + ContainerName: "ctr", + ResourceName: "example.com/gpu", + RequestName: "container-1-request-0", + }, + }, + }, + }), + ), + old: *podtest.MakePod("foo"), + test: "valid duplicate container name,, extended resource name in requestMapping in ExtendedResourceClaimStatus", }, { new: *podtest.MakePod("foo", podtest.SetContainers(podtest.MakeContainer("ctr", podtest.SetContainerResources( @@ -15855,6 +16003,16 @@ func TestValidatePodStatusUpdate(t *testing.T) { ), err: `status.ephemeralContainerStatuses[0].user.linux: Forbidden: cannot be set for a windows pod`, test: "containerUser cannot be set for windows pod in ephemeralContainerStatuses", + }, { + new: *podtest.MakePod("foo", + podtest.SetStatus(core.PodStatus{ + Conditions: []core.PodCondition{{ + Type: core.AllContainersRestarting, + Status: core.ConditionTrue, + }}, + }), + ), + old: *podtest.MakePod("foo"), }, } @@ -17578,9 +17736,14 @@ func TestValidateServiceCreate(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PreferSameTrafficDistribution, tc.newTrafficDist) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedServiceNameValidation, tc.relaxedServiceNames) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, !tc.legacyIPs) + if !tc.newTrafficDist { + featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.34")) + } + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.PreferSameTrafficDistribution: tc.newTrafficDist, + features.RelaxedServiceNameValidation: tc.relaxedServiceNames, + features.StrictIPCIDRValidation: !tc.legacyIPs, + }) svc := makeValidService() tc.tweakSvc(&svc) errs := ValidateServiceCreate(&svc) @@ -17964,7 +18127,7 @@ func TestValidateReplicationControllerUpdate(t *testing.T) { rc.Spec.Selector = invalid }), expectedErrs: field.ErrorList{ - field.Invalid(field.NewPath("spec.template.labels"), nil, "").WithOrigin("labelKey"), + field.Invalid(field.NewPath("spec.template.labels"), nil, "").WithOrigin("format=k8s-label-key"), }, }, "invalid pod": { @@ -18113,7 +18276,7 @@ func TestValidateReplicationController(t *testing.T) { } }), expectedErrs: field.ErrorList{ - field.Invalid(field.NewPath("metadata.labels"), nil, "").WithOrigin("labelKey"), + field.Invalid(field.NewPath("metadata.labels"), nil, "").WithOrigin("format=k8s-label-key"), }, }, "invalid label 2": { @@ -18124,7 +18287,7 @@ func TestValidateReplicationController(t *testing.T) { }), expectedErrs: field.ErrorList{ field.Invalid(field.NewPath("spec.template.metadata.labels"), nil, "does not match template"), - field.Invalid(field.NewPath("spec.template.labels"), nil, "").WithOrigin("labelKey"), + field.Invalid(field.NewPath("spec.template.labels"), nil, "").WithOrigin("format=k8s-label-key"), }, }, "invalid annotation": { @@ -18134,7 +18297,7 @@ func TestValidateReplicationController(t *testing.T) { } }), expectedErrs: field.ErrorList{ - field.Invalid(field.NewPath("metadata.annotations"), nil, "name part must consist of"), + field.Invalid(field.NewPath("metadata.annotations"), nil, "").WithOrigin("format=k8s-label-key"), }, }, "invalid restart policy 1": { @@ -20302,8 +20465,10 @@ func TestValidateServiceUpdate(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, true) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedServiceNameValidation, tc.relaxedServiceNames) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.StrictIPCIDRValidation: true, + features.RelaxedServiceNameValidation: tc.relaxedServiceNames, + }) oldSvc := makeValidService() newSvc := makeValidService() @@ -22824,6 +22989,7 @@ func TestValidateOSFields(t *testing.T) { "Overhead", "Tolerations", "TopologySpreadConstraints", + "WorkloadRef", ) expect := sets.NewString().Union(osSpecificFields).Union(osNeutralFields) @@ -24207,8 +24373,10 @@ func TestCrossNamespaceSource(t *testing.T) { } for _, tc := range testCases { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, true) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, true) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.AnyVolumeDataSource: true, + features.CrossNamespaceVolumeDataSource: true, + }) opts := PersistentVolumeClaimSpecValidationOptions{} if tc.expectedFail { if errs := ValidatePersistentVolumeClaimSpec(tc.claimSpec, field.NewPath("spec"), opts); len(errs) == 0 { @@ -24469,7 +24637,7 @@ func TestValidateTopologySpreadConstraints(t *testing.T) { MatchLabelKeys: []string{"/simple"}, }}, wantFieldErrors: field.ErrorList{ - field.Invalid(fieldPathMatchLabelKeys.Index(0), nil, "").WithOrigin("labelKey"), + field.Invalid(fieldPathMatchLabelKeys.Index(0), nil, "").WithOrigin("format=k8s-label-key"), }, opts: PodValidationOptions{ AllowMatchLabelKeysInPodTopologySpread: true, @@ -24485,7 +24653,7 @@ func TestValidateTopologySpreadConstraints(t *testing.T) { MatchLabelKeys: []string{"/simple"}, }}, wantFieldErrors: field.ErrorList{ - field.Invalid(fieldPathMatchLabelKeys.Index(0), nil, "").WithOrigin("labelKey"), + field.Invalid(fieldPathMatchLabelKeys.Index(0), nil, "").WithOrigin("format=k8s-label-key"), }, opts: PodValidationOptions{ AllowMatchLabelKeysInPodTopologySpread: true, @@ -24584,7 +24752,7 @@ func TestValidateTopologySpreadConstraints(t *testing.T) { LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"}}, }}, wantFieldErrors: field.ErrorList{ - field.Invalid(labelSelectorField.Child("matchLabels"), nil, "").WithOrigin("labelKey"), + field.Invalid(labelSelectorField.Child("matchLabels"), nil, "").WithOrigin("format=k8s-label-key"), }, opts: PodValidationOptions{ AllowMatchLabelKeysInPodTopologySpread: true, @@ -26093,7 +26261,7 @@ func TestValidatePodHostName(t *testing.T) { HostnameOverride: ptr.To(""), }, expectedErrs: field.ErrorList{ - field.Invalid(field.NewPath("spec.hostnameOverride"), "", "RFC 1123"), + field.Invalid(field.NewPath("spec.hostnameOverride"), nil, "").WithOrigin("format=k8s-long-name"), }, }, { @@ -26132,7 +26300,7 @@ func TestValidatePodHostName(t *testing.T) { HostnameOverride: ptr.To("Not-RFC1123"), }, expectedErrs: field.ErrorList{ - field.Invalid(field.NewPath("spec.hostnameOverride"), "", "RFC 1123"), + field.Invalid(field.NewPath("spec.hostnameOverride"), nil, "").WithOrigin("format=k8s-long-name"), }, }, { @@ -26912,17 +27080,14 @@ func TestValidateLoadBalancerStatus(t *testing.T) { testCases := []struct { name string - ipModeEnabled bool legacyIPs bool - nonLBAllowed bool tweakOldLBStatus func(s *core.LoadBalancerStatus) tweakLBStatus func(s *core.LoadBalancerStatus) tweakSvcSpec func(s *core.ServiceSpec) numErrs int }{ { - name: "type is not LB", - nonLBAllowed: false, + name: "type is not LB", tweakSvcSpec: func(s *core.ServiceSpec) { s.Type = core.ServiceTypeClusterIP }, @@ -26933,20 +27098,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 1, }, { - name: "type is not LB. back-compat", - nonLBAllowed: true, - tweakSvcSpec: func(s *core.ServiceSpec) { - s.Type = core.ServiceTypeClusterIP - }, - tweakLBStatus: func(s *core.LoadBalancerStatus) { - s.Ingress = []core.LoadBalancerIngress{{ - IP: "1.2.3.4", - }} - }, - numErrs: 0, - }, { - name: "valid vip ipMode", - ipModeEnabled: true, + name: "valid vip ipMode", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "1.2.3.4", @@ -26955,8 +27107,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 0, }, { - name: "valid proxy ipMode", - ipModeEnabled: true, + name: "valid proxy ipMode", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "1.2.3.4", @@ -26965,8 +27116,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 0, }, { - name: "invalid ipMode", - ipModeEnabled: true, + name: "invalid ipMode", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "1.2.3.4", @@ -26975,8 +27125,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 1, }, { - name: "missing ipMode with LoadbalancerIPMode enabled", - ipModeEnabled: true, + name: "missing ipMode", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "1.2.3.4", @@ -26984,17 +27133,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 1, }, { - name: "missing ipMode with LoadbalancerIPMode disabled", - ipModeEnabled: false, - tweakLBStatus: func(s *core.LoadBalancerStatus) { - s.Ingress = []core.LoadBalancerIngress{{ - IP: "1.2.3.4", - }} - }, - numErrs: 0, - }, { - name: "missing ip with ipMode present", - ipModeEnabled: true, + name: "missing ip with ipMode present", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IPMode: &ipModeProxy, @@ -27002,9 +27141,8 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 1, }, { - name: "legacy IP with legacy validation", - ipModeEnabled: true, - legacyIPs: true, + name: "legacy IP with legacy validation", + legacyIPs: true, tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "001.002.003.004", @@ -27013,8 +27151,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { }, numErrs: 0, }, { - name: "legacy IP with strict validation", - ipModeEnabled: true, + name: "legacy IP with strict validation", tweakLBStatus: func(s *core.LoadBalancerStatus) { s.Ingress = []core.LoadBalancerIngress{{ IP: "001.002.003.004", @@ -27074,15 +27211,7 @@ func TestValidateLoadBalancerStatus(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - if !tc.ipModeEnabled { - featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, version.MustParse("1.31")) - } else { - // (This feature gate doesn't exist in 1.31 so we can't set it - // when testing !ipModeEnabled.) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, !tc.legacyIPs) - } - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AllowServiceLBStatusOnNonLB, tc.nonLBAllowed) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, !tc.legacyIPs) oldStatus := core.LoadBalancerStatus{} if tc.tweakOldLBStatus != nil { tc.tweakOldLBStatus(&oldStatus) @@ -27857,6 +27986,60 @@ func TestValidatePodResize(t *testing.T) { new *core.Pod err string }{ + { + test: "pod-level resources resize with nil resources in old pod", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("200m", "", "", ""), + }))), + podtest.SetPodResources(nil), + ), + err: "", + }, + { + test: "pod-level resources resize with nil requests in old pod", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("", "", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("", "200Mi", "", "")}), + ), + err: "", + }, + { + test: "pod-level resources resize with nil limits in old pod", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("100m", "100Mi", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("100m", "200Mi", "", "")}), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{ + Limits: getResources("", "", "", ""), + }))), + podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("", "200Mi", "", "")}), + ), + err: "", + }, { test: "pod-level resources with container cpu limit change", new: podtest.MakePod("pod", @@ -27873,7 +28056,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with container memory limit change", new: podtest.MakePod("pod", @@ -27890,7 +28073,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with container cpu request change", @@ -27908,7 +28091,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with container memory request change", new: podtest.MakePod("pod", @@ -27925,7 +28108,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with pod-level memory limit change", @@ -27943,7 +28126,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("200m", "100Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with pod-level memory request change", @@ -27961,7 +28144,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("200m", "100Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with pod-level cpu limit change", @@ -27979,7 +28162,7 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Limits: getResources("100m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", }, { test: "pod-level resources with pod-level cpu request change", @@ -27997,7 +28180,31 @@ func TestValidatePodResize(t *testing.T) { }))), podtest.SetPodResources(&core.ResourceRequirements{Requests: getResources("200m", "200Mi", "", "")}), ), - err: "pods with pod-level resources cannot be resized", + err: "", + }, + { + test: "pod-level resources with pod-level storage changes", + new: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{Requests: getResources("100m", "100Mi", "", "")}), + podtest.SetContainerResizePolicy(core.ContainerResizePolicy{ResourceName: core.ResourceMemory, RestartPolicy: core.NotRequired}), + )), + podtest.SetPodResources(&core.ResourceRequirements{ + Requests: getResources("100m", "100Mi", "", "200Mi"), + Limits: getResources("100m", "100Mi", "", ""), + }), + ), + old: podtest.MakePod("pod", + podtest.SetContainers(podtest.MakeContainer("container", + podtest.SetContainerResources(core.ResourceRequirements{Requests: getResources("100m", "100Mi", "", "")}), + podtest.SetContainerResizePolicy(core.ContainerResizePolicy{ResourceName: core.ResourceMemory, RestartPolicy: core.NotRequired}), + )), + podtest.SetPodResources(&core.ResourceRequirements{ + Requests: getResources("200m", "100Mi", "", "100Mi"), + Limits: getResources("100m", "200Mi", "", ""), + }), + ), + err: "spec: Forbidden: only cpu and memory resources are mutable", }, { test: "cpu limit change", @@ -28241,6 +28448,11 @@ func TestValidatePodResize(t *testing.T) { old: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "200Mi", "", ""), core.ContainerRestartPolicyAlways), new: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "100Mi", "", ""), core.ContainerRestartPolicyAlways), err: "", + }, { + test: "memory limit decrease for sidecar containers, resize policy RestartContainer", + old: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "200Mi", "", ""), core.ContainerRestartPolicyAlways, resizePolicy(core.ResourceMemory, core.RestartContainer)), + new: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "100Mi", "", ""), core.ContainerRestartPolicyAlways, resizePolicy(core.ResourceMemory, core.RestartContainer)), + err: "", }, { test: "storage limit change for sidecar containers", old: mkPodWithInitContainers(core.ResourceList{}, getResources("100m", "100Mi", "2Gi", ""), core.ContainerRestartPolicyAlways), @@ -28348,6 +28560,21 @@ func TestValidatePodResize(t *testing.T) { old: mkPodWithInitContainers(getResources("100m", "0", "1Gi", ""), core.ResourceList{}, core.ContainerRestartPolicyAlways, resizePolicy(core.ResourceMemory, core.RestartContainer)), new: mkPodWithInitContainers(getResources("100m", "0", "2Gi", ""), core.ResourceList{}, core.ContainerRestartPolicyAlways, resizePolicy(core.ResourceMemory, core.NotRequired)), err: "spec: Forbidden: only cpu and memory resources are mutable", + }, { + test: "invalid: adding non-resizable resources to a container without resources", + old: podtest.MakePod("pod", podtest.SetContainers( + podtest.MakeContainer("c1"), + )), + new: podtest.MakePod("pod", podtest.SetContainers( + podtest.MakeContainer("c1", + podtest.SetContainerResources(core.ResourceRequirements{ + Requests: core.ResourceList{ + core.ResourceEphemeralStorage: resource.MustParse("10Gi"), + }, + }), + ), + )), + err: "spec: Forbidden: only cpu and memory resources are mutable", }, } @@ -28378,7 +28605,8 @@ func TestValidatePodResize(t *testing.T) { test.old.Spec.RestartPolicy = "Always" } - errs := ValidatePodResize(test.new, test.old, PodValidationOptions{AllowSidecarResizePolicy: true}) + errs := ValidatePodResize(test.new, test.old, PodValidationOptions{AllowSidecarResizePolicy: true, InPlacePodLevelResourcesVerticalScalingEnabled: true, PodLevelResourcesEnabled: true}) + if test.err == "" { if len(errs) != 0 { t.Errorf("unexpected invalid: (%+v)\nA: %+v\nB: %+v", errs, test.new, test.old) @@ -29073,9 +29301,10 @@ func TestValidateContainerRestartPolicy(t *testing.T) { podRestartPolicyAlways := core.RestartPolicyAlways successCases := []struct { - Name string - RestartPolicy *core.ContainerRestartPolicy - RestartPolicyRules []core.ContainerRestartRule + Name string + RestartPolicy *core.ContainerRestartPolicy + RestartPolicyRules []core.ContainerRestartRule + AllowRestartAllContainers bool }{ { Name: "no-restart-policy-and-rules", @@ -29099,6 +29328,17 @@ func TestValidateContainerRestartPolicy(t *testing.T) { Values: []int32{42}, }, }}, + }, { + Name: "restart-all-containers", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{{ + Action: "RestartAllContainers", + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: "In", + Values: []int32{42}, + }, + }}, + AllowRestartAllContainers: true, }, } @@ -29114,6 +29354,7 @@ func TestValidateContainerRestartPolicy(t *testing.T) { }} opts := PodValidationOptions{ AllowContainerRestartPolicyRules: true, + AllowRestartAllContainers: tc.AllowRestartAllContainers, } errs := validateContainers(containers, podOS, volumeDevices, nil, defaultGracePeriod, field.NewPath("containers"), opts, &podRestartPolicyAlways, noUserNamespace) if len(errs) > 0 { @@ -29262,28 +29503,71 @@ func TestValidateContainerRestartPolicy(t *testing.T) { }) } - // test cases sidecar containers + containerRestartPolicyAlways := core.ContainerRestartPolicyAlways + // test cases init containers cases := []struct { title string + restartPolicy *core.ContainerRestartPolicy restartPolicyRules []core.ContainerRestartRule + opts PodValidationOptions expectedErrors field.ErrorList }{ { "sidecar containers without restart policy rules", + &containerRestartPolicyAlways, nil, + PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + }, nil, }, { - "restart policy rules are not supported with restart policy Always", + "restart policy rules are supported with restart policy Always with option", + &containerRestartPolicyAlways, []core.ContainerRestartRule{{ - Action: core.ContainerRestartRuleActionRestart, + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{1}, + }, + }}, + PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + AllowRestartAllContainers: true, + }, + nil, + }, + { + "restart policy rules are not supported with restart policy Always without option", + &containerRestartPolicyAlways, + []core.ContainerRestartRule{{ + Action: core.ContainerRestartRuleActionRestartAllContainers, ExitCodes: &core.ContainerRestartRuleOnExitCodes{ Operator: core.ContainerRestartRuleOnExitCodesOpIn, Values: []int32{1}, }, }}, + PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + }, field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].restartPolicyRules", BadValue: ""}}, }, + { + "restart policy rules are not supported if no restart policy is set", + nil, + []core.ContainerRestartRule{{ + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{1}, + }, + }}, + PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + AllowRestartAllContainers: true, + }, + field.ErrorList{{Type: field.ErrorTypeRequired, Field: "initContainers[0].restartPolicy", BadValue: ""}}, + }, } for _, tc := range cases { @@ -29293,13 +29577,10 @@ func TestValidateContainerRestartPolicy(t *testing.T) { Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", - RestartPolicy: &containerRestartPolicyAlways, + RestartPolicy: tc.restartPolicy, RestartPolicyRules: tc.restartPolicyRules, }} - opts := PodValidationOptions{ - AllowContainerRestartPolicyRules: true, - } - errs := validateInitContainers(containers, podOS, nil, volumeDevices, nil, defaultGracePeriod, field.NewPath("initContainers"), opts, &podRestartPolicyAlways, noUserNamespace) + errs := validateInitContainers(containers, podOS, nil, volumeDevices, nil, defaultGracePeriod, field.NewPath("initContainers"), tc.opts, &podRestartPolicyAlways, noUserNamespace) if tc.expectedErrors == nil { if len(errs) > 0 { t.Errorf("unexpected errors: %v", prettyErrorList(errs)) @@ -29414,13 +29695,258 @@ func TestValidateContainerStateTransition(t *testing.T) { newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, expectErr: true, }, + { + name: "restart allowed if the container have RestartAllContainers actions", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + Containers: []core.Container{ + { + Name: "c1", + Image: "image", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{ + { + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }, + }, + }, + }, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + }, + { + name: "restart allowed if other container have RestartAllContainers actions", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + Containers: []core.Container{ + container1RestartNever, + { + Name: "c2", + Image: "image", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{ + { + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }, + }, + }, + }, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ContainerRestartRules, true) + opts := PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + AllowRestartAllContainers: true, + } + errs := ValidateContainerStateTransition(tc.newStatuses, tc.oldStatuses, field.NewPath("field"), tc.podSpec, opts) - errs := ValidateContainerStateTransition(tc.newStatuses, tc.oldStatuses, field.NewPath("field"), tc.podSpec) + if tc.expectErr && len(errs) == 0 { + t.Errorf("Unexpected success") + } + if !tc.expectErr && len(errs) > 0 { + t.Errorf("Unexpected error(s): %v", errs) + } + }) + } +} + +func TestValidateInitContainerStateTransition(t *testing.T) { + var ( + containerRestartPolicyAlways = core.ContainerRestartPolicyAlways + containerRestartPolicyNever = core.ContainerRestartPolicyNever + ) + runningState := core.ContainerState{Running: &core.ContainerStateRunning{}} + terminatedState := func(exitCode int32) core.ContainerState { + return core.ContainerState{Terminated: &core.ContainerStateTerminated{ExitCode: exitCode}} + } + + container1 := core.Container{Name: "c1", Image: "image"} + container1RestartNever := core.Container{Name: "c1", Image: "image", RestartPolicy: &containerRestartPolicyNever} + container1RestartAlways := core.Container{Name: "c1", Image: "image", RestartPolicy: &containerRestartPolicyAlways} + container1RestartRuleIn42 := core.Container{ + Name: "c1", + Image: "image", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{ + { + Action: core.ContainerRestartRuleActionRestart, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: "In", + Values: []int32{42}, + }, + }, + }, + } + + testCases := []struct { + name string + podSpec core.PodSpec + oldStatuses []core.ContainerStatus + newStatuses []core.ContainerStatus + expectErr bool + }{ + { + name: "feature enabled, not terminated", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{container1RestartNever}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: false, + }, + { + name: "feature enabled, restart allowed by pod policy 'Always'", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyAlways, + InitContainers: []core.Container{container1}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(0)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: false, + }, + { + name: "feature enabled, restart allowed by pod policy 'OnFailure' with exit 1", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyOnFailure, + InitContainers: []core.Container{container1}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: false, + }, + { + name: "feature enabled, restart not allowed by pod policy 'OnFailure' with exit 0", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyOnFailure, + InitContainers: []core.Container{container1}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(0)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: true, + }, + { + name: "feature enabled, restart not allowed by pod policy 'Never', with transition", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{container1}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(0)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: true, + }, + { + name: "feature enabled, restart allowed by container policy 'Always'", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{container1RestartAlways}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(0)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: false, + }, + { + name: "feature enabled, restart not allowed by container policy 'Never'", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyAlways, + InitContainers: []core.Container{container1RestartNever}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(0)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: true, + }, + { + name: "feature enabled, restart allowed by container rule", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{container1RestartRuleIn42}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(42)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: false, + }, + { + name: "feature enabled, restart not allowed by container rule mismatch", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{container1RestartRuleIn42}, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + expectErr: true, + }, + { + name: "restart allowed if the container have RestartAllContainers actions", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{ + { + Name: "c1", + Image: "image", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{ + { + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }, + }, + }, + }, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + }, + { + name: "restart allowed if other container have RestartAllContainers actions", + podSpec: core.PodSpec{ + RestartPolicy: core.RestartPolicyNever, + InitContainers: []core.Container{ + container1RestartNever, + { + Name: "c2", + Image: "image", + RestartPolicy: &containerRestartPolicyNever, + RestartPolicyRules: []core.ContainerRestartRule{ + { + Action: core.ContainerRestartRuleActionRestartAllContainers, + ExitCodes: &core.ContainerRestartRuleOnExitCodes{ + Operator: core.ContainerRestartRuleOnExitCodesOpIn, + Values: []int32{42}, + }, + }, + }, + }, + }, + }, + oldStatuses: []core.ContainerStatus{{Name: "c1", State: terminatedState(1)}}, + newStatuses: []core.ContainerStatus{{Name: "c1", State: runningState}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + opts := PodValidationOptions{ + AllowContainerRestartPolicyRules: true, + AllowRestartAllContainers: true, + } + errs := ValidateInitContainerStateTransition(tc.newStatuses, tc.oldStatuses, field.NewPath("field"), tc.podSpec, opts) if tc.expectErr && len(errs) == 0 { t.Errorf("Unexpected success") @@ -29431,3 +29957,392 @@ func TestValidateContainerStateTransition(t *testing.T) { }) } } + +func TestNumericTolerationsWithFeatureGate(t *testing.T) { + testCases := []struct { + name string + toleration core.Toleration + featureGateOn bool + errorMsg string + }{ + { + name: "Gt operator with valid numeric value and feature gate enabled", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpGt, + Value: "950", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + }, + { + name: "Lt operator with valid numeric value and feature gate enabled", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpLt, + Value: "800", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + }, + { + name: "Gt operator with negative numeric value and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "-100", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + }, + { + name: "Gt operator with non-numeric value and feature gate enabled", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpGt, + Value: "high", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "high"), + }, + { + name: "Gt operator with leading zeros and feature gate enabled (invalid - strict validation)", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpGt, + Value: "0950", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "0950"), + }, + { + name: "Gt operator with value '0' and feature gate enabled (valid)", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "0", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + }, + { + name: "Gt operator with decimal value and feature gate enabled", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpGt, + Value: "95.5", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "95.5"), + }, + { + name: "Gt operator with just minus sign and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "-", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "-"), + }, + { + name: "Gt operator with plus sign and feature gate enabled (invalid - strict validation)", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "+100", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "+100"), + }, + { + name: "Gt operator with space in value and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "95 0", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be a valid decimal integer in canonical form", "95 0"), + }, + { + name: "Gt operator with empty value and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: must be non-empty", ""), + }, + { + name: "Lt operator with feature gate disabled", + toleration: core.Toleration{ + Key: "node.kubernetes.io/sla", + Operator: core.TolerationOpLt, + Value: "950", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: false, + errorMsg: "Unsupported value", + }, + { + name: "Gt operator with max int64 value and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "9223372036854775807", + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + }, + { + name: "Gt operator with overflow value and feature gate enabled", + toleration: core.Toleration{ + Key: "test-key", + Operator: core.TolerationOpGt, + Value: "9223372036854775808", // max int64 + 1 + Effect: core.TaintEffectNoSchedule, + }, + featureGateOn: true, + errorMsg: fmt.Sprintf("tolerations[0].value: Invalid value: %q: strconv.ParseInt: parsing %q: value out of range", "9223372036854775808", "9223372036854775808"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintTolerationComparisonOperators, tc.featureGateOn) + + opts := PodValidationOptions{ + AllowTaintTolerationComparisonOperators: tc.featureGateOn, + } + errs := ValidateTolerations([]core.Toleration{tc.toleration}, field.NewPath("tolerations"), opts) + + if tc.errorMsg != "" { + if len(errs) == 0 { + t.Errorf("Expected error but got none") + } else if !strings.Contains(errs.ToAggregate().Error(), tc.errorMsg) { + t.Errorf("Expected error message to contain %q, got %q", tc.errorMsg, errs.ToAggregate().Error()) + } + } else { + if len(errs) > 0 { + t.Errorf("Unexpected error(s): %v", errs) + } + } + }) + } +} + +func TestAllRegistedNodeDeclaredFeatures(t *testing.T) { + // Test that feature registry is valid. + for _, feature := range ndf.AllFeatures { + if err := validateNodeDeclaredFeatureName(feature.Name()); err != nil { + t.Fatalf("ValidateFeatures() = %v, want nil", err) + } + } + // Soft limit to catch potential uncontrolled growth of node registered features. + const maxNodeDeclaredFeatures = 128 + if len(ndf.AllFeatures) > maxNodeDeclaredFeatures { + t.Errorf("Number of registered node declared features (%d) exceeds the limit of %d. Please review if this is expected and update the threshold if necessary.", len(ndf.AllFeatures), maxNodeDeclaredFeatures) + } +} + +func TestValidateNodeDeclaredFeatures(t *testing.T) { + makeNode := func(declaredFeatures []string) *core.Node { + node := makeNode("test-node", nil) + node.Status.DeclaredFeatures = declaredFeatures + node.ObjectMeta.ResourceVersion = "1" + return &node + } + testCases := []struct { + name string + declaredFeatures []string + expectErr bool + expectedErrType field.ErrorType + expectedErrPath string + expectedErrMsg string + }{ + { + name: "empty feature list", + declaredFeatures: []string{}, + expectErr: false, + }, + { + name: "valid features", + declaredFeatures: []string{"AnotherFeature", "MyFeature", "MyFeature/SubFeature"}, + expectErr: false, + }, + { + name: "duplicate feature names", + declaredFeatures: []string{"MyFeature", "MyFeature"}, + expectErr: true, + expectedErrType: field.ErrorTypeDuplicate, + expectedErrPath: "status.declaredFeatures[1]", + expectedErrMsg: "Duplicate value", + }, + { + name: "unsorted feature names", + declaredFeatures: []string{"MyFeature", "AnotherFeature"}, + expectErr: true, + expectedErrType: field.ErrorTypeInvalid, + expectedErrPath: "status.declaredFeatures[1]", + expectedErrMsg: "list must be sorted alphabetically", + }, + { + name: "mixed valid and invalid", + declaredFeatures: []string{"AnotherFeature", "invalid", "MyFeature"}, + expectErr: true, + }, + { + name: "invalid feature name format", + declaredFeatures: []string{"myFeature"}, + expectErr: true, + expectedErrType: field.ErrorTypeInvalid, + expectedErrPath: "status.declaredFeatures[0]", + expectedErrMsg: "invalid feature name", + }, + { + name: "invalid feature name - kebab-case", + declaredFeatures: []string{"Feature-name"}, + expectErr: true, + expectedErrType: field.ErrorTypeInvalid, + expectedErrPath: "status.declaredFeatures[0]", + expectedErrMsg: "invalid feature name", + }, + { + name: "invalid feature name - ends with slash", + declaredFeatures: []string{"FeatureA/"}, + expectErr: true, + expectedErrType: field.ErrorTypeInvalid, + expectedErrPath: "status.declaredFeatures[0]", + expectedErrMsg: "invalid feature name", + }, + { + name: "invalid feature name - name too long", + declaredFeatures: []string{"F" + strings.Repeat("e", validation.DNS1123SubdomainMaxLength)}, + expectErr: true, + expectedErrType: field.ErrorTypeInvalid, + expectedErrPath: "status.declaredFeatures[0]", + expectedErrMsg: "invalid feature name", + }, + } + + for _, tc := range testCases { + for _, op := range []string{"create", "update"} { + t.Run(strings.Join([]string{tc.name, op}, "-"), func(t *testing.T) { + var errs field.ErrorList + if op == "create" { + errs = ValidateNode(makeNode(tc.declaredFeatures)) + } else { + errs = ValidateNodeUpdate(makeNode(tc.declaredFeatures), makeNode([]string{})) + } + if tc.expectErr { + if len(errs) == 0 { + t.Errorf("Expected error but got none") + } else { + found := false + for _, err := range errs { + t.Logf("DEBUG: tc.expectedErrType: %v err.Type:%v err.Field :%v tc.expectedErrPath:%v", tc.expectedErrType, err.Type, err.Field, tc.expectedErrPath) + t.Logf("DEBUG: err.Type == tc.expectedErrType:%v err.Field == tc.expectedErrPath:%v", err.Type == tc.expectedErrType, err.Field == tc.expectedErrPath) + t.Logf("DEBUG: tc.expectedErrMsg: %v err.Error() :%v contains:%v", tc.expectedErrMsg, err.Error(), strings.Contains(err.Error(), tc.expectedErrMsg)) + if tc.expectedErrType != "" && err.Type == tc.expectedErrType && err.Field == tc.expectedErrPath { + if tc.expectedErrMsg != "" && strings.Contains(err.Error(), tc.expectedErrMsg) { + found = true + break + } else if tc.expectedErrMsg == "" { + found = true + break + } + } else if tc.expectedErrType == "" { + found = true + break + } + } + if !found && tc.expectErr { + t.Errorf("Expected error with type `%s` on field `%s` containing `%q` but got `%v`", tc.expectedErrType, tc.expectedErrPath, tc.expectedErrMsg, errs) + } + } + } else if len(errs) > 0 { + t.Errorf("Unexpected error: %v", errs) + } + }) + } + } +} + +func TestValidateWorkloadReference(t *testing.T) { + successCases := map[string]*core.Pod{ + "correct": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: "group", + PodGroupReplicaKey: "replica", + })), + "no replica key": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: "group", + PodGroupReplicaKey: "", + })), + } + for name, pod := range successCases { + errs := ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, field.NewPath("field"), PodValidationOptions{}) + if len(errs) != 0 { + t.Errorf("Expected success for %q: %v", name, errs) + } + } + + failureCases := map[string]*core.Pod{ + "empty workload name": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "", + PodGroup: "group", + PodGroupReplicaKey: "replica", + })), + "incorrect workload name": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: ".workload", + PodGroup: "group", + PodGroupReplicaKey: "replica", + })), + "too long workload name": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: strings.Repeat("w", 254), + PodGroup: "group", + PodGroupReplicaKey: "replica", + })), + "empty pod group": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: "", + PodGroupReplicaKey: "replica", + })), + "incorrect pod group": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: ".group", + PodGroupReplicaKey: "replica", + })), + "too long pod group": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: strings.Repeat("g", 64), + PodGroupReplicaKey: "replica", + })), + "incorrect replica key": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: "group", + PodGroupReplicaKey: ".replica", + })), + "too long replica key": podtest.MakePod("", podtest.SetWorkloadRef(&core.WorkloadReference{ + Name: "workload", + PodGroup: "group", + PodGroupReplicaKey: strings.Repeat("r", 64), + })), + } + for name, pod := range failureCases { + errs := ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, field.NewPath("field"), PodValidationOptions{}) + if len(errs) == 0 { + t.Errorf("Expected failure for %q", name) + } + } +} diff --git a/pkg/apis/core/zz_generated.deepcopy.go b/pkg/apis/core/zz_generated.deepcopy.go index de1b33b9b82f9..017ac70cd420a 100644 --- a/pkg/apis/core/zz_generated.deepcopy.go +++ b/pkg/apis/core/zz_generated.deepcopy.go @@ -3147,6 +3147,11 @@ func (in *NodeStatus) DeepCopyInto(out *NodeStatus) { *out = new(NodeFeatures) (*in).DeepCopyInto(*out) } + if in.DeclaredFeatures != nil { + in, out := &in.DeclaredFeatures, &out.DeclaredFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } return } @@ -3905,6 +3910,13 @@ func (in *PodCertificateProjection) DeepCopyInto(out *PodCertificateProjection) *out = new(int32) **out = **in } + if in.UserAnnotations != nil { + in, out := &in.UserAnnotations, &out.UserAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } @@ -4559,6 +4571,11 @@ func (in *PodSpec) DeepCopyInto(out *PodSpec) { *out = new(string) **out = **in } + if in.WorkloadRef != nil { + in, out := &in.WorkloadRef, &out.WorkloadRef + *out = new(WorkloadReference) + **out = **in + } return } @@ -4629,6 +4646,18 @@ func (in *PodStatus) DeepCopyInto(out *PodStatus) { *out = new(PodExtendedResourceClaimStatus) (*in).DeepCopyInto(*out) } + if in.AllocatedResources != nil { + in, out := &in.AllocatedResources, &out.AllocatedResources + *out = make(ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } + if in.Resources != nil { + in, out := &in.Resources, &out.Resources + *out = new(ResourceRequirements) + (*in).DeepCopyInto(*out) + } return } @@ -6834,3 +6863,19 @@ func (in *WindowsSecurityContextOptions) DeepCopy() *WindowsSecurityContextOptio in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadReference) DeepCopyInto(out *WorkloadReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadReference. +func (in *WorkloadReference) DeepCopy() *WorkloadReference { + if in == nil { + return nil + } + out := new(WorkloadReference) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/discovery/types.go b/pkg/apis/discovery/types.go index c31c8f19ee83f..de78ceeed28d5 100644 --- a/pkg/apis/discovery/types.go +++ b/pkg/apis/discovery/types.go @@ -138,10 +138,7 @@ type EndpointHints struct { ForZones []ForZone // forNodes indicates the node(s) this endpoint should be consumed by when - // using topology aware routing. - // This is an Alpha feature and is only used when the PreferSameTrafficDistribution - // feature gate is enabled. May contain a maximum of 8 entries. - // +featureGate=PreferSameTrafficDistribution + // using topology aware routing. May contain a maximum of 8 entries. ForNodes []ForNode } diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go index 511adaf5d6977..31e5b29a2f59b 100644 --- a/pkg/apis/networking/validation/validation.go +++ b/pkg/apis/networking/validation/validation.go @@ -707,7 +707,20 @@ func allowRelaxedServiceNameValidation(oldIngress *networking.Ingress) bool { if oldIngress == nil { return false } - // If feature gate is disabled, check if any service names in the old Ingresss + + // Check if default backend service names in the old Ingress + if oldIngress.Spec.DefaultBackend != nil && oldIngress.Spec.DefaultBackend.Service != nil { + serviceName := oldIngress.Spec.DefaultBackend.Service.Name + // If a name doesn't validate with apimachineryvalidation.NameIsDNS1035Label, but does validate with apimachineryvalidation.NameIsDNSLabel, + // then we allow it to be used as a Service name in an Ingress. + dnsLabelValidationErrors := apimachineryvalidation.NameIsDNSLabel(serviceName, false) + dns1035LabelValidationErrors := apimachineryvalidation.NameIsDNS1035Label(serviceName, false) + if len(dnsLabelValidationErrors) == 0 && len(dns1035LabelValidationErrors) > 0 { + return true + } + } + + // If feature gate is disabled, check if any service names in the old Ingress for _, rule := range oldIngress.Spec.Rules { if rule.HTTP == nil { continue diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go index 888904e693784..43a48de45e61c 100644 --- a/pkg/apis/networking/validation/validation_test.go +++ b/pkg/apis/networking/validation/validation_test.go @@ -1663,6 +1663,23 @@ func TestValidateIngressUpdate(t *testing.T) { }, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("backend").Child("service").Child("name"), "1-test-service", `a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`)}, }, + "update defaultBackend service to conform to relaxed service name - RelaxedServiceNameValidation disabled": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + oldIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + newIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "1-test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + }, + expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("defaultBackend").Child("service").Child("name"), "1-test-service", `a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`)}, + }, "update service to conform to relaxed service name - RelaxedServiceNameValidation enabled": { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { oldIngress.Spec.Rules = []networking.IngressRule{{ @@ -1702,6 +1719,23 @@ func TestValidateIngressUpdate(t *testing.T) { }, relaxedServiceName: true, }, + "update defaultBackend service to conform to relaxed service name - RelaxedServiceNameValidation enabled": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + oldIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + newIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "1-test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + }, + relaxedServiceName: true, + }, "updating an already existing relaxed validation service name with RelaxedServiceNameValidation disabled": { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { oldIngress.Spec.Rules = []networking.IngressRule{{ @@ -1740,6 +1774,22 @@ func TestValidateIngressUpdate(t *testing.T) { }} }, }, + "updating an already existing relaxed validation defaultBackend service name with RelaxedServiceNameValidation disabled": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + oldIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "1-test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + newIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "2-test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + }, + }, "updating an already existing relaxed validation service name to a non-relaxed name with RelaxedServiceNameValidation disabled": { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { oldIngress.Spec.Rules = []networking.IngressRule{{ @@ -1778,6 +1828,22 @@ func TestValidateIngressUpdate(t *testing.T) { }} }, }, + "updating an already existing relaxed validation defaultBackend service name to a non-relaxed name with RelaxedServiceNameValidation disabled": { + tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { + oldIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "1-test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + newIngress.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: "test-service", + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + }, + }, } for name, testCase := range testCases { @@ -2896,6 +2962,19 @@ func TestAllowRelaxedServiceNameValidation(t *testing.T) { return &networking.Ingress{Spec: networking.IngressSpec{Rules: rules}} } + ingressWithDefaultBackend := func(defaultBackendName string, ruleServiceNames ...string) *networking.Ingress { + ing := basicIngress(ruleServiceNames...) + if defaultBackendName != "" { + ing.Spec.DefaultBackend = &networking.IngressBackend{ + Service: &networking.IngressServiceBackend{ + Name: defaultBackendName, + Port: networking.ServiceBackendPort{Number: 80}, + }, + } + } + return ing + } + tests := []struct { name string ingress *networking.Ingress @@ -2926,14 +3005,43 @@ func TestAllowRelaxedServiceNameValidation(t *testing.T) { ingress: basicIngress("validname", "1abc-def"), expect: true, }, + { + name: "defaultBackend with valid DNS1035 name", + ingress: ingressWithDefaultBackend("validname"), + expect: false, + }, + { + name: "defaultBackend with DNS1123 valid but DNS1035 invalid name (starts with digit)", + ingress: ingressWithDefaultBackend("1-default-service"), + expect: true, + }, + { + name: "defaultBackend relaxed name with valid rules", + ingress: ingressWithDefaultBackend("1-default", "valid-rule-service"), + expect: true, + }, + { + name: "only rules have relaxed name, defaultBackend is valid", + ingress: ingressWithDefaultBackend("valid-default", "1-rule-service"), + expect: true, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + // Test feature with gate disabled + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedServiceNameValidation, false) got := allowRelaxedServiceNameValidation(tc.ingress) if got != tc.expect { t.Errorf("allowRelaxedServiceNameValidation() = %v, want %v", got, tc.expect) } + + // Test feature with gate enabled - it should always return true + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedServiceNameValidation, true) + got = allowRelaxedServiceNameValidation(tc.ingress) + if got != true { + t.Errorf("allowRelaxedServiceNameValidation() = %v, want %v", got, true) + } }) } } diff --git a/pkg/apis/node/validation/validation.go b/pkg/apis/node/validation/validation.go index d66e82b93825f..58b80b55f1cee 100644 --- a/pkg/apis/node/validation/validation.go +++ b/pkg/apis/node/validation/validation.go @@ -68,7 +68,7 @@ func validateScheduling(s *node.Scheduling, fldPath *field.Path) field.ErrorList } func validateTolerations(tolerations []core.Toleration, fldPath *field.Path) field.ErrorList { - allErrs := corevalidation.ValidateTolerations(tolerations, fldPath.Child("tolerations")) + allErrs := corevalidation.ValidateTolerations(tolerations, fldPath, corevalidation.PodValidationOptions{}) // Ensure uniquenes of tolerations. tolerationSet := map[core.Toleration]bool{} for i, t := range tolerations { diff --git a/pkg/apis/rbac/v1/doc.go b/pkg/apis/rbac/v1/doc.go index ab4d30713847d..73977b4234e37 100644 --- a/pkg/apis/rbac/v1/doc.go +++ b/pkg/apis/rbac/v1/doc.go @@ -19,6 +19,8 @@ limitations under the License. // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/rbac/v1 // +k8s:deepcopy-gen=package +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/rbac/v1 // +groupName=rbac.authorization.k8s.io diff --git a/pkg/apis/rbac/v1/zz_generated.validations.go b/pkg/apis/rbac/v1/zz_generated.validations.go new file mode 100644 index 0000000000000..0e53d6e0b7e57 --- /dev/null +++ b/pkg/apis/rbac/v1/zz_generated.validations.go @@ -0,0 +1,240 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + fmt "fmt" + + rbacv1 "k8s.io/api/rbac/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type ClusterRoleBinding + scheme.AddValidationFunc((*rbacv1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRoleBinding), safe.Cast[*rbacv1.ClusterRoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ClusterRoleBindingList + scheme.AddValidationFunc((*rbacv1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1.ClusterRoleBindingList), safe.Cast[*rbacv1.ClusterRoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBinding + scheme.AddValidationFunc((*rbacv1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1.RoleBinding), safe.Cast[*rbacv1.RoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBindingList + scheme.AddValidationFunc((*rbacv1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1.RoleBindingList), safe.Cast[*rbacv1.RoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRoleBinding) (errs field.ErrorList) { + // field rbacv1.ClusterRoleBinding.TypeMeta has no validation + // field rbacv1.ClusterRoleBinding.ObjectMeta has no validation + + // field rbacv1.ClusterRoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBinding) []rbacv1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1.ClusterRoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBinding) *rbacv1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.ClusterRoleBindingList) (errs field.ErrorList) { + // field rbacv1.ClusterRoleBindingList.TypeMeta has no validation + // field rbacv1.ClusterRoleBindingList.ListMeta has no validation + + // field rbacv1.ClusterRoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.ClusterRoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.ClusterRoleBindingList) []rbacv1.ClusterRoleBinding { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBinding validates an instance of RoleBinding according +// to declarative validation rules in the API schema. +func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleBinding) (errs field.ErrorList) { + // field rbacv1.RoleBinding.TypeMeta has no validation + // field rbacv1.RoleBinding.ObjectMeta has no validation + + // field rbacv1.RoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1.RoleBinding) []rbacv1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1.RoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1.RoleBinding) *rbacv1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBindingList validates an instance of RoleBindingList according +// to declarative validation rules in the API schema. +func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleBindingList) (errs field.ErrorList) { + // field rbacv1.RoleBindingList.TypeMeta has no validation + // field rbacv1.RoleBindingList.ListMeta has no validation + + // field rbacv1.RoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1.RoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1.RoleBindingList) []rbacv1.RoleBinding { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_RoleRef validates an instance of RoleRef according +// to declarative validation rules in the API schema. +func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.RoleRef) (errs field.ErrorList) { + // field rbacv1.RoleRef.APIGroup has no validation + // field rbacv1.RoleRef.Kind has no validation + + // field rbacv1.RoleRef.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1.RoleRef) *string { return &oldObj.Name }), oldObj != nil)...) + + return errs +} + +// Validate_Subject validates an instance of Subject according +// to declarative validation rules in the API schema. +func Validate_Subject(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1.Subject) (errs field.ErrorList) { + // field rbacv1.Subject.Kind has no validation + // field rbacv1.Subject.APIGroup has no validation + + // field rbacv1.Subject.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1.Subject) *string { return &oldObj.Name }), oldObj != nil)...) + + // field rbacv1.Subject.Namespace has no validation + return errs +} diff --git a/pkg/apis/rbac/v1alpha1/doc.go b/pkg/apis/rbac/v1alpha1/doc.go index 77246de7585ce..ab97d04370614 100644 --- a/pkg/apis/rbac/v1alpha1/doc.go +++ b/pkg/apis/rbac/v1alpha1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/rbac/v1alpha1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/rbac/v1alpha1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/rbac/v1alpha1 // +groupName=rbac.authorization.k8s.io diff --git a/pkg/apis/rbac/v1alpha1/zz_generated.validations.go b/pkg/apis/rbac/v1alpha1/zz_generated.validations.go new file mode 100644 index 0000000000000..0f517283ac189 --- /dev/null +++ b/pkg/apis/rbac/v1alpha1/zz_generated.validations.go @@ -0,0 +1,242 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + context "context" + fmt "fmt" + + rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type ClusterRoleBinding + scheme.AddValidationFunc((*rbacv1alpha1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRoleBinding), safe.Cast[*rbacv1alpha1.ClusterRoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ClusterRoleBindingList + scheme.AddValidationFunc((*rbacv1alpha1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.ClusterRoleBindingList), safe.Cast[*rbacv1alpha1.ClusterRoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBinding + scheme.AddValidationFunc((*rbacv1alpha1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.RoleBinding), safe.Cast[*rbacv1alpha1.RoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBindingList + scheme.AddValidationFunc((*rbacv1alpha1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1alpha1.RoleBindingList), safe.Cast[*rbacv1alpha1.RoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRoleBinding) (errs field.ErrorList) { + // field rbacv1alpha1.ClusterRoleBinding.TypeMeta has no validation + // field rbacv1alpha1.ClusterRoleBinding.ObjectMeta has no validation + + // field rbacv1alpha1.ClusterRoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBinding) []rbacv1alpha1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1alpha1.ClusterRoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBinding) *rbacv1alpha1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.ClusterRoleBindingList) (errs field.ErrorList) { + // field rbacv1alpha1.ClusterRoleBindingList.TypeMeta has no validation + // field rbacv1alpha1.ClusterRoleBindingList.ListMeta has no validation + + // field rbacv1alpha1.ClusterRoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.ClusterRoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.ClusterRoleBindingList) []rbacv1alpha1.ClusterRoleBinding { + return oldObj.Items + }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBinding validates an instance of RoleBinding according +// to declarative validation rules in the API schema. +func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleBinding) (errs field.ErrorList) { + // field rbacv1alpha1.RoleBinding.TypeMeta has no validation + // field rbacv1alpha1.RoleBinding.ObjectMeta has no validation + + // field rbacv1alpha1.RoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBinding) []rbacv1alpha1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1alpha1.RoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBinding) *rbacv1alpha1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBindingList validates an instance of RoleBindingList according +// to declarative validation rules in the API schema. +func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleBindingList) (errs field.ErrorList) { + // field rbacv1alpha1.RoleBindingList.TypeMeta has no validation + // field rbacv1alpha1.RoleBindingList.ListMeta has no validation + + // field rbacv1alpha1.RoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1alpha1.RoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleBindingList) []rbacv1alpha1.RoleBinding { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_RoleRef validates an instance of RoleRef according +// to declarative validation rules in the API schema. +func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.RoleRef) (errs field.ErrorList) { + // field rbacv1alpha1.RoleRef.APIGroup has no validation + // field rbacv1alpha1.RoleRef.Kind has no validation + + // field rbacv1alpha1.RoleRef.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1alpha1.RoleRef) *string { return &oldObj.Name }), oldObj != nil)...) + + return errs +} + +// Validate_Subject validates an instance of Subject according +// to declarative validation rules in the API schema. +func Validate_Subject(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1alpha1.Subject) (errs field.ErrorList) { + // field rbacv1alpha1.Subject.Kind has no validation + // field rbacv1alpha1.Subject.APIVersion has no validation + + // field rbacv1alpha1.Subject.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1alpha1.Subject) *string { return &oldObj.Name }), oldObj != nil)...) + + // field rbacv1alpha1.Subject.Namespace has no validation + return errs +} diff --git a/pkg/apis/rbac/v1beta1/doc.go b/pkg/apis/rbac/v1beta1/doc.go index b3474dc74b5b5..815253236c41d 100644 --- a/pkg/apis/rbac/v1beta1/doc.go +++ b/pkg/apis/rbac/v1beta1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/rbac/v1beta1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/rbac/v1beta1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/rbac/v1beta1 // +groupName=rbac.authorization.k8s.io diff --git a/pkg/apis/rbac/v1beta1/zz_generated.validations.go b/pkg/apis/rbac/v1beta1/zz_generated.validations.go new file mode 100644 index 0000000000000..82e44b20bc192 --- /dev/null +++ b/pkg/apis/rbac/v1beta1/zz_generated.validations.go @@ -0,0 +1,240 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1beta1 + +import ( + context "context" + fmt "fmt" + + rbacv1beta1 "k8s.io/api/rbac/v1beta1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type ClusterRoleBinding + scheme.AddValidationFunc((*rbacv1beta1.ClusterRoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRoleBinding), safe.Cast[*rbacv1beta1.ClusterRoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ClusterRoleBindingList + scheme.AddValidationFunc((*rbacv1beta1.ClusterRoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ClusterRoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.ClusterRoleBindingList), safe.Cast[*rbacv1beta1.ClusterRoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBinding + scheme.AddValidationFunc((*rbacv1beta1.RoleBinding)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBinding(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.RoleBinding), safe.Cast[*rbacv1beta1.RoleBinding](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type RoleBindingList + scheme.AddValidationFunc((*rbacv1beta1.RoleBindingList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_RoleBindingList(ctx, op, nil /* fldPath */, obj.(*rbacv1beta1.RoleBindingList), safe.Cast[*rbacv1beta1.RoleBindingList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_ClusterRoleBinding validates an instance of ClusterRoleBinding according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRoleBinding) (errs field.ErrorList) { + // field rbacv1beta1.ClusterRoleBinding.TypeMeta has no validation + // field rbacv1beta1.ClusterRoleBinding.ObjectMeta has no validation + + // field rbacv1beta1.ClusterRoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBinding) []rbacv1beta1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1beta1.ClusterRoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBinding) *rbacv1beta1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_ClusterRoleBindingList validates an instance of ClusterRoleBindingList according +// to declarative validation rules in the API schema. +func Validate_ClusterRoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.ClusterRoleBindingList) (errs field.ErrorList) { + // field rbacv1beta1.ClusterRoleBindingList.TypeMeta has no validation + // field rbacv1beta1.ClusterRoleBindingList.ListMeta has no validation + + // field rbacv1beta1.ClusterRoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.ClusterRoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ClusterRoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.ClusterRoleBindingList) []rbacv1beta1.ClusterRoleBinding { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBinding validates an instance of RoleBinding according +// to declarative validation rules in the API schema. +func Validate_RoleBinding(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleBinding) (errs field.ErrorList) { + // field rbacv1beta1.RoleBinding.TypeMeta has no validation + // field rbacv1beta1.RoleBinding.ObjectMeta has no validation + + // field rbacv1beta1.RoleBinding.Subjects + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.Subject, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Subject)...) + return + }(fldPath.Child("subjects"), obj.Subjects, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBinding) []rbacv1beta1.Subject { return oldObj.Subjects }), oldObj != nil)...) + + // field rbacv1beta1.RoleBinding.RoleRef + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_RoleRef(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("roleRef"), &obj.RoleRef, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBinding) *rbacv1beta1.RoleRef { return &oldObj.RoleRef }), oldObj != nil)...) + + return errs +} + +// Validate_RoleBindingList validates an instance of RoleBindingList according +// to declarative validation rules in the API schema. +func Validate_RoleBindingList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleBindingList) (errs field.ErrorList) { + // field rbacv1beta1.RoleBindingList.TypeMeta has no validation + // field rbacv1beta1.RoleBindingList.ListMeta has no validation + + // field rbacv1beta1.RoleBindingList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []rbacv1beta1.RoleBinding, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_RoleBinding)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleBindingList) []rbacv1beta1.RoleBinding { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_RoleRef validates an instance of RoleRef according +// to declarative validation rules in the API schema. +func Validate_RoleRef(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.RoleRef) (errs field.ErrorList) { + // field rbacv1beta1.RoleRef.APIGroup has no validation + // field rbacv1beta1.RoleRef.Kind has no validation + + // field rbacv1beta1.RoleRef.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1beta1.RoleRef) *string { return &oldObj.Name }), oldObj != nil)...) + + return errs +} + +// Validate_Subject validates an instance of Subject according +// to declarative validation rules in the API schema. +func Validate_Subject(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *rbacv1beta1.Subject) (errs field.ErrorList) { + // field rbacv1beta1.Subject.Kind has no validation + // field rbacv1beta1.Subject.APIGroup has no validation + + // field rbacv1beta1.Subject.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *rbacv1beta1.Subject) *string { return &oldObj.Name }), oldObj != nil)...) + + // field rbacv1beta1.Subject.Namespace has no validation + return errs +} diff --git a/pkg/apis/rbac/validation/validation.go b/pkg/apis/rbac/validation/validation.go index b65ac19f72e2b..fcc672dbd2a84 100644 --- a/pkg/apis/rbac/validation/validation.go +++ b/pkg/apis/rbac/validation/validation.go @@ -143,7 +143,7 @@ func ValidateRoleBinding(roleBinding *rbac.RoleBinding) field.ErrorList { } if len(roleBinding.RoleRef.Name) == 0 { - allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "")) + allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "").MarkCoveredByDeclarative()) } else { for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) { allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg)) @@ -187,7 +187,7 @@ func ValidateClusterRoleBinding(roleBinding *rbac.ClusterRoleBinding) field.Erro } if len(roleBinding.RoleRef.Name) == 0 { - allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "")) + allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), "").MarkCoveredByDeclarative()) } else { for _, msg := range ValidateRBACName(roleBinding.RoleRef.Name, false) { allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg)) @@ -218,7 +218,7 @@ func ValidateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath allErrs := field.ErrorList{} if len(subject.Name) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) + allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative()) } switch subject.Kind { diff --git a/pkg/apis/resource/install/install.go b/pkg/apis/resource/install/install.go index a747f62003dc7..ab936326c5ff4 100644 --- a/pkg/apis/resource/install/install.go +++ b/pkg/apis/resource/install/install.go @@ -40,6 +40,5 @@ func Install(scheme *runtime.Scheme) { utilruntime.Must(v1beta1.AddToScheme(scheme)) utilruntime.Must(v1beta2.AddToScheme(scheme)) utilruntime.Must(v1.AddToScheme(scheme)) - // TODO (https://github.com/kubernetes/kubernetes/issues/133131): put v1 first in 1.35 - utilruntime.Must(scheme.SetVersionPriority(v1beta2.SchemeGroupVersion, v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion, v1alpha3.SchemeGroupVersion)) + utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion, v1alpha3.SchemeGroupVersion)) } diff --git a/pkg/apis/resource/types.go b/pkg/apis/resource/types.go index faac04e6dc8f5..35933110d67ec 100644 --- a/pkg/apis/resource/types.go +++ b/pkg/apis/resource/types.go @@ -145,10 +145,13 @@ type ResourceSliceSpec struct { // Devices lists some or all of the devices in this pool. // - // Must not have more than 128 entries. + // Must not have more than 128 entries. If any device uses taints or consumes counters the limit is 64. + // + // Only one of Devices and SharedCounters can be set in a ResourceSlice. // // +optional // +listType=atomic + // +zeroOrOneOf=ResourceSliceType Devices []Device // PerDeviceNodeSelection defines whether the access from nodes to @@ -166,19 +169,22 @@ type ResourceSliceSpec struct { // SharedCounters defines a list of counter sets, each of which // has a name and a list of counters available. // - // The names of the SharedCounters must be unique in the ResourceSlice. + // The names of the counter sets must be unique in the ResourcePool. + // + // Only one of Devices and SharedCounters can be set in a ResourceSlice. // - // The maximum number of counters in all sets is 32. + // The maximum number of counter sets is 8. // // +optional // +listType=atomic // +featureGate=DRAPartitionableDevices + // +zeroOrOneOf=ResourceSliceType SharedCounters []CounterSet } // CounterSet defines a named set of counters // that are available to be used by devices defined in the -// ResourceSlice. +// ResourcePool. // // The counters are not allocatable by themselves, but // can be referenced by devices. When a device is allocated, @@ -194,7 +200,7 @@ type CounterSet struct { // Counters defines the set of counters for this CounterSet // The name of each counter must be unique in that set and must be a DNS label. // - // The maximum number of counters in all sets is 32. + // The maximum number of counters is 32. // // +required Counters map[string]Counter @@ -243,13 +249,27 @@ type ResourcePool struct { const ResourceSliceMaxSharedCapacity = 128 const ResourceSliceMaxDevices = 128 +const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64 const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name. const BindingConditionsMaxSize = 4 const BindingFailureConditionsMaxSize = 4 -// Defines the max number of shared counters that can be specified -// in a ResourceSlice. The number is summed up across all sets. -const ResourceSliceMaxSharedCounters = 32 +// Defines the maximum number of counter sets (through the +// SharedCounters field) that can be defined in a ResourceSlice. +const ResourceSliceMaxCounterSets = 8 + +// Defines the maximum number of counters that can be defined +// in a counter set. +const ResourceSliceMaxCountersPerCounterSet = 32 + +// Defines the maximum number of device counter consumptions +// (through the ConsumesCounters field) that can be defined per +// device. +const ResourceSliceMaxDeviceCounterConsumptionsPerDevice = 2 + +// Defines the maximum number of counters that can be defined +// per device counter consumption. +const ResourceSliceMaxCountersPerDeviceCounterConsumption = 32 // Device represents one individual hardware instance that can be selected based // on its attributes. Besides the name, exactly one field must be set. @@ -282,10 +302,8 @@ type Device struct { // // There can only be a single entry per counterSet. // - // The total number of device counter consumption entries - // must be <= 32. In addition, the total number in the - // entire ResourceSlice must be <= 1024 (for example, - // 64 devices with 16 counters each). + // The maximum number of device counter consumptions per + // device is 2. // // +optional // +listType=atomic @@ -326,7 +344,9 @@ type Device struct { // If specified, these are the driver-defined taints. // - // The maximum number of taints is 4. + // The maximum number of taints is 16. If taints are set for + // any device in a ResourceSlice, then the maximum number of + // allowed devices per ResourceSlice is 64 instead of 128. // // This is an alpha field and requires enabling the DRADeviceTaints // feature gate. @@ -402,10 +422,7 @@ type DeviceCounterConsumption struct { // Counters defines the counters that will be consumed by the device. // - // The maximum number counters in a device is 32. - // In addition, the maximum number of all counters - // in all devices is 1024 (for example, 64 devices with - // 16 counters each). + // The maximum number of counters is 32. // // +required Counters map[string]Counter @@ -531,14 +548,6 @@ type CapacityRequestPolicyRange struct { // Limit for the sum of the number of entries in both attributes and capacity. const ResourceSliceMaxAttributesAndCapacitiesPerDevice = 32 -// Limit for the total number of counters in each device. -const ResourceSliceMaxCountersPerDevice = 32 - -// Limit for the total number of counters defined in devices in -// a ResourceSlice. We want to allow up to 64 devices to specify -// up to 16 counters, so the limit for the ResourceSlice will be 1024. -const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16 - // QualifiedName is the name of a device attribute or capacity. // // Attributes and capacities are defined either by the owner of the specific @@ -601,8 +610,8 @@ type DeviceAttribute struct { // DeviceAttributeMaxValueLength is the maximum length of a string or version attribute value. const DeviceAttributeMaxValueLength = 64 -// DeviceTaintsMaxLength is the maximum number of taints per device. -const DeviceTaintsMaxLength = 4 +// DeviceTaintsMaxLength is the maximum number of taints per Device. +const DeviceTaintsMaxLength = 16 // The device this taint is attached to has the "effect" on // any claim which does not tolerate the taint and, through the claim, @@ -622,8 +631,10 @@ type DeviceTaint struct { // The effect of the taint on claims that do not tolerate the taint // and through such claims on the pods using them. - // Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for - // nodes is not valid here. + // + // Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for + // nodes is not valid here. More effects may get added in the future. + // Consumers must treat unknown effects like None. // // +required Effect DeviceTaintEffect @@ -632,6 +643,14 @@ type DeviceTaint struct { // // Implementing PreferNoSchedule would depend on a scoring solution for DRA. // It might get added as part of that. + // + // A possible future new effect is NoExecuteWithPodDisruptionBudget: + // honor the pod disruption budget instead of simply deleting pods. + // This is currently undecided, it could also be a separate field. + // + // Validation must be prepared to allow unknown enums in stored objects, + // which will enable adding new enums within a single release without + // ratcheting. // TimeAdded represents the time at which the taint was added. // Added automatically during create or update if not set. @@ -650,6 +669,9 @@ type DeviceTaint struct { type DeviceTaintEffect string const ( + // No effect, the taint is purely informational. + DeviceTaintEffectNone DeviceTaintEffect = "None" + // Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, // but allow all pods submitted to Kubelet without going through the scheduler // to start, and allow all already-running pods to continue running. @@ -1602,8 +1624,8 @@ type AllocationConfigSource string // Valid [DeviceAllocationConfiguration.Source] values. const ( - AllocationConfigSourceClass = "FromClass" - AllocationConfigSourceClaim = "FromClaim" + AllocationConfigSourceClass AllocationConfigSource = "FromClass" + AllocationConfigSourceClaim AllocationConfigSource = "FromClaim" ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -1876,18 +1898,16 @@ type DeviceTaintRule struct { // Changing the spec automatically increments the metadata.generation number. Spec DeviceTaintRuleSpec - // ^^^ - // A spec gets added because adding a status seems likely. - // Such a status could provide feedback on applying the - // eviction and/or statistics (number of matching devices, - // affected allocated claims, pods remaining to be evicted, - // etc.). + // Status provides information about what was requested in the spec. + // + // +optional + Status DeviceTaintRuleStatus } // DeviceTaintRuleSpec specifies the selector and one taint. type DeviceTaintRuleSpec struct { // DeviceSelector defines which device(s) the taint is applied to. - // All selector criteria must be satified for a device to + // All selector criteria must be satisfied for a device to // match. The empty selector matches all devices. Without // a selector, no devices are matches. // @@ -1904,13 +1924,6 @@ type DeviceTaintRuleSpec struct { // The empty selector matches all devices. Without a selector, no devices // are matched. type DeviceTaintSelector struct { - // If DeviceClassName is set, the selectors defined there must be - // satisfied by a device to be selected. This field corresponds - // to class.metadata.name. - // - // +optional - DeviceClassName *string - // If driver is set, only devices from that driver are selected. // This fields corresponds to slice.spec.driver. // @@ -1937,16 +1950,43 @@ type DeviceTaintSelector struct { // // +optional Device *string +} - // Selectors contains the same selection criteria as a ResourceClaim. - // Currently, CEL expressions are supported. All of these selectors - // must be satisfied. +// DeviceTaintRuleStatus provides information about an on-going pod eviction. +type DeviceTaintRuleStatus struct { + // Conditions provide information about the state of the DeviceTaintRule + // and the cluster at some point in time, + // in a machine-readable and human-readable format. + // + // The following condition is currently defined as part of this API, more may + // get added: + // - Type: EvictionInProgress + // - Status: True if there are currently pods which need to be evicted, False otherwise + // (includes the effects which don't cause eviction). + // - Reason: not specified, may change + // - Message: includes information about number of pending pods and already evicted pods + // in a human-readable format, updated periodically, may change + // + // For `effect: None`, the condition above gets set once for each change to + // the spec, with the message containing information about what would happen + // if the effect was `NoExecute`. This feedback can be used to decide whether + // changing the effect to `NoExecute` will work as intended. It only gets + // set once to avoid having to constantly update the status. + // + // Must have 8 or less entries. // // +optional - // +listType=atomic - Selectors []DeviceSelector + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition } +// DeviceTaintRuleStatusMaxConditions is the maximum number of conditions in DeviceTaintRuleStatus. +const DeviceTaintRuleStatusMaxConditions = 8 + +// DeviceTaintConditionEvictionInProgress is the publicly documented condition type for the DeviceTaintRuleStatus. +const DeviceTaintConditionEvictionInProgress = "EvictionInProgress" + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // DeviceTaintRuleList is a collection of DeviceTaintRules. diff --git a/pkg/apis/resource/v1/doc.go b/pkg/apis/resource/v1/doc.go index 9166fefdbd613..d608dbef1ef4d 100644 --- a/pkg/apis/resource/v1/doc.go +++ b/pkg/apis/resource/v1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1 // Package v1 is the v1 version of the resource API. package v1 diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go new file mode 100644 index 0000000000000..a3e1655353dc7 --- /dev/null +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -0,0 +1,1822 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + fmt "fmt" + + resourcev1 "k8s.io/api/resource/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type DeviceClass + scheme.AddValidationFunc((*resourcev1.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1.DeviceClass), safe.Cast[*resourcev1.DeviceClass](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type DeviceClassList + scheme.AddValidationFunc((*resourcev1.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1.DeviceClassList), safe.Cast[*resourcev1.DeviceClassList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaim + scheme.AddValidationFunc((*resourcev1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/", "/status": + return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaim), safe.Cast[*resourcev1.ResourceClaim](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimList + scheme.AddValidationFunc((*resourcev1.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaimList), safe.Cast[*resourcev1.ResourceClaimList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplate + scheme.AddValidationFunc((*resourcev1.ResourceClaimTemplate)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplate(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaimTemplate), safe.Cast[*resourcev1.ResourceClaimTemplate](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplateList + scheme.AddValidationFunc((*resourcev1.ResourceClaimTemplateList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplateList(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaimTemplateList), safe.Cast[*resourcev1.ResourceClaimTemplateList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSlice + scheme.AddValidationFunc((*resourcev1.ResourceSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSlice(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceSlice), safe.Cast[*resourcev1.ResourceSlice](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSliceList + scheme.AddValidationFunc((*resourcev1.ResourceSliceList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSliceList(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceSliceList), safe.Cast[*resourcev1.ResourceSliceList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_AllocatedDeviceStatus validates an instance of AllocatedDeviceStatus according +// to declarative validation rules in the API schema. +func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocatedDeviceStatus) (errs field.ErrorList) { + // field resourcev1.AllocatedDeviceStatus.Driver has no validation + // field resourcev1.AllocatedDeviceStatus.Pool has no validation + // field resourcev1.AllocatedDeviceStatus.Device has no validation + + // field resourcev1.AllocatedDeviceStatus.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1.AllocatedDeviceStatus) *string { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1.AllocatedDeviceStatus.Conditions has no validation + // field resourcev1.AllocatedDeviceStatus.Data has no validation + + // field resourcev1.AllocatedDeviceStatus.NetworkData + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.NetworkDeviceData, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_NetworkDeviceData(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("networkData"), obj.NetworkData, safe.Field(oldObj, func(oldObj *resourcev1.AllocatedDeviceStatus) *resourcev1.NetworkDeviceData { + return oldObj.NetworkData + }), oldObj != nil)...) + + return errs +} + +var symbolsForAllocationConfigSource = sets.New(resourcev1.AllocationConfigSourceClaim, resourcev1.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + +// Validate_AllocationResult validates an instance of AllocationResult according +// to declarative validation rules in the API schema. +func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocationResult) (errs field.ErrorList) { + // field resourcev1.AllocationResult.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.AllocationResult) *resourcev1.DeviceAllocationResult { return &oldObj.Devices }), oldObj != nil)...) + + // field resourcev1.AllocationResult.NodeSelector has no validation + // field resourcev1.AllocationResult.AllocationTimestamp has no validation + return errs +} + +// Validate_CounterSet validates an instance of CounterSet according +// to declarative validation rules in the API schema. +func Validate_CounterSet(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.CounterSet) (errs field.ErrorList) { + // field resourcev1.CounterSet.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *resourcev1.CounterSet) *string { return &oldObj.Name }), oldObj != nil)...) + + // field resourcev1.CounterSet.Counters has no validation + return errs +} + +// Validate_Device validates an instance of Device according +// to declarative validation rules in the API schema. +func Validate_Device(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.Device) (errs field.ErrorList) { + // field resourcev1.Device.Name has no validation + + // field resourcev1.Device.Attributes + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[resourcev1.QualifiedName]resourcev1.DeviceAttribute, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the map and call the value type's validation function + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute)...) + return + }(fldPath.Child("attributes"), obj.Attributes, safe.Field(oldObj, func(oldObj *resourcev1.Device) map[resourcev1.QualifiedName]resourcev1.DeviceAttribute { + return oldObj.Attributes + }), oldObj != nil)...) + + // field resourcev1.Device.Capacity has no validation + + // field resourcev1.Device.ConsumesCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceCounterConsumption, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceCounterConsumption, b resourcev1.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceCounterConsumption, b resourcev1.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption)...) + return + }(fldPath.Child("consumesCounters"), obj.ConsumesCounters, safe.Field(oldObj, func(oldObj *resourcev1.Device) []resourcev1.DeviceCounterConsumption { return oldObj.ConsumesCounters }), oldObj != nil)...) + + // field resourcev1.Device.NodeName has no validation + // field resourcev1.Device.NodeSelector has no validation + // field resourcev1.Device.AllNodes has no validation + + // field resourcev1.Device.Taints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceTaint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceTaint)...) + return + }(fldPath.Child("taints"), obj.Taints, safe.Field(oldObj, func(oldObj *resourcev1.Device) []resourcev1.DeviceTaint { return oldObj.Taints }), oldObj != nil)...) + + // field resourcev1.Device.BindsToNode has no validation + + // field resourcev1.Device.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1.Device) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1.Device.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1.Device) []string { return oldObj.BindingFailureConditions }), oldObj != nil)...) + + // field resourcev1.Device.AllowMultipleAllocations has no validation + return errs +} + +// Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationConfiguration) (errs field.ErrorList) { + // field resourcev1.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.AllocationConfigSource, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationConfiguration) *resourcev1.AllocationConfigSource { + return &oldObj.Source + }), oldObj != nil)...) + + // field resourcev1.DeviceAllocationConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1.DeviceAllocationConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationConfiguration) *resourcev1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +var symbolsForDeviceAllocationMode = sets.New(resourcev1.DeviceAllocationModeAll, resourcev1.DeviceAllocationModeExactCount) + +// Validate_DeviceAllocationMode validates an instance of DeviceAllocationMode according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationMode(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationMode) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceAllocationMode, nil)...) + + return errs +} + +// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationResult) (errs field.ErrorList) { + // field resourcev1.DeviceAllocationResult.Results + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceRequestAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequestAllocationResult)...) + return + }(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationResult) []resourcev1.DeviceRequestAllocationResult { + return oldObj.Results + }), oldObj != nil)...) + + // field resourcev1.DeviceAllocationResult.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceAllocationConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 64); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceAllocationConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationResult) []resourcev1.DeviceAllocationConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + return errs +} + +var unionMembershipFor_k8s_io_api_resource_v1_DeviceAttribute_ = validate.NewUnionMembership(validate.NewUnionMember("int"), validate.NewUnionMember("bool"), validate.NewUnionMember("string"), validate.NewUnionMember("version")) + +// Validate_DeviceAttribute validates an instance of DeviceAttribute according +// to declarative validation rules in the API schema. +func Validate_DeviceAttribute(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAttribute) (errs field.ErrorList) { + errs = append(errs, validate.Union(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_resource_v1_DeviceAttribute_, func(obj *resourcev1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.IntValue != nil + }, func(obj *resourcev1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.BoolValue != nil + }, func(obj *resourcev1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.StringValue != nil + }, func(obj *resourcev1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.VersionValue != nil + })...) + + // field resourcev1.DeviceAttribute.IntValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int64, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("int"), obj.IntValue, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAttribute) *int64 { return oldObj.IntValue }), oldObj != nil)...) + + // field resourcev1.DeviceAttribute.BoolValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *bool, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bool"), obj.BoolValue, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAttribute) *bool { return oldObj.BoolValue }), oldObj != nil)...) + + // field resourcev1.DeviceAttribute.StringValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("string"), obj.StringValue, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAttribute) *string { return oldObj.StringValue }), oldObj != nil)...) + + // field resourcev1.DeviceAttribute.VersionValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("version"), obj.VersionValue, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAttribute) *string { return oldObj.VersionValue }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaim validates an instance of DeviceClaim according +// to declarative validation rules in the API schema. +func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClaim) (errs field.ErrorList) { + // field resourcev1.DeviceClaim.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceRequest, b resourcev1.DeviceRequest) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceRequest, b resourcev1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaim) []resourcev1.DeviceRequest { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1.DeviceClaim.Constraints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceConstraint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceConstraint)...) + return + }(fldPath.Child("constraints"), obj.Constraints, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaim) []resourcev1.DeviceConstraint { return oldObj.Constraints }), oldObj != nil)...) + + // field resourcev1.DeviceClaim.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceClaimConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClaimConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaim) []resourcev1.DeviceClaimConfiguration { return oldObj.Config }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaimConfiguration validates an instance of DeviceClaimConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClaimConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClaimConfiguration) (errs field.ErrorList) { + // field resourcev1.DeviceClaimConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaimConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1.DeviceClaimConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaimConfiguration) *resourcev1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClass validates an instance of DeviceClass according +// to declarative validation rules in the API schema. +func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClass) (errs field.ErrorList) { + // field resourcev1.DeviceClass.TypeMeta has no validation + + // field resourcev1.DeviceClass.ObjectMeta + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *metav1.ObjectMeta, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + func() { // cohort name + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *metav1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.OptionalValue); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *metav1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.LongName)...) + }() + return + }(fldPath.Child("metadata"), &obj.ObjectMeta, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClass) *metav1.ObjectMeta { return &oldObj.ObjectMeta }), oldObj != nil)...) + + // field resourcev1.DeviceClass.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClass) *resourcev1.DeviceClassSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassConfiguration validates an instance of DeviceClassConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClassConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassConfiguration) (errs field.ErrorList) { + // field resourcev1.DeviceClassConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassConfiguration) *resourcev1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassList validates an instance of DeviceClassList according +// to declarative validation rules in the API schema. +func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassList) (errs field.ErrorList) { + // field resourcev1.DeviceClassList.TypeMeta has no validation + // field resourcev1.DeviceClassList.ListMeta has no validation + + // field resourcev1.DeviceClassList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceClass, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClass)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassList) []resourcev1.DeviceClass { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according +// to declarative validation rules in the API schema. +func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassSpec) (errs field.ErrorList) { + // field resourcev1.DeviceClassSpec.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassSpec) []resourcev1.DeviceSelector { return oldObj.Selectors }), oldObj != nil)...) + + // field resourcev1.DeviceClassSpec.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceClassConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClassConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassSpec) []resourcev1.DeviceClassConfiguration { return oldObj.Config }), oldObj != nil)...) + + // field resourcev1.DeviceClassSpec.ExtendedResourceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ExtendedResourceName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("extendedResourceName"), obj.ExtendedResourceName, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassSpec) *string { return oldObj.ExtendedResourceName }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConfiguration validates an instance of DeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceConfiguration) (errs field.ErrorList) { + // field resourcev1.DeviceConfiguration.Opaque + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.OpaqueDeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_OpaqueDeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("opaque"), obj.Opaque, safe.Field(oldObj, func(oldObj *resourcev1.DeviceConfiguration) *resourcev1.OpaqueDeviceConfiguration { + return oldObj.Opaque + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConstraint validates an instance of DeviceConstraint according +// to declarative validation rules in the API schema. +func Validate_DeviceConstraint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceConstraint) (errs field.ErrorList) { + // field resourcev1.DeviceConstraint.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1.DeviceConstraint) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1.DeviceConstraint.MatchAttribute + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.FullyQualifiedName, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourceFullyQualifiedName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("matchAttribute"), obj.MatchAttribute, safe.Field(oldObj, func(oldObj *resourcev1.DeviceConstraint) *resourcev1.FullyQualifiedName { return oldObj.MatchAttribute }), oldObj != nil)...) + + // field resourcev1.DeviceConstraint.DistinctAttribute has no validation + return errs +} + +// Validate_DeviceCounterConsumption validates an instance of DeviceCounterConsumption according +// to declarative validation rules in the API schema. +func Validate_DeviceCounterConsumption(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceCounterConsumption) (errs field.ErrorList) { + // field resourcev1.DeviceCounterConsumption.CounterSet + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("counterSet"), &obj.CounterSet, safe.Field(oldObj, func(oldObj *resourcev1.DeviceCounterConsumption) *string { return &oldObj.CounterSet }), oldObj != nil)...) + + // field resourcev1.DeviceCounterConsumption.Counters has no validation + return errs +} + +// Validate_DeviceRequest validates an instance of DeviceRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceRequest) (errs field.ErrorList) { + // field resourcev1.DeviceRequest.Name has no validation + + // field resourcev1.DeviceRequest.Exactly + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ExactDeviceRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ExactDeviceRequest(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("exactly"), obj.Exactly, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequest) *resourcev1.ExactDeviceRequest { return oldObj.Exactly }), oldObj != nil)...) + + // field resourcev1.DeviceRequest.FirstAvailable + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceSubRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceSubRequest, b resourcev1.DeviceSubRequest) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceSubRequest, b resourcev1.DeviceSubRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceSubRequest)...) + return + }(fldPath.Child("firstAvailable"), obj.FirstAvailable, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequest) []resourcev1.DeviceSubRequest { return oldObj.FirstAvailable }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceRequestAllocationResult) (errs field.ErrorList) { + // field resourcev1.DeviceRequestAllocationResult.Request has no validation + + // field resourcev1.DeviceRequestAllocationResult.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.Pool + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) *string { return &oldObj.Pool }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.Device has no validation + // field resourcev1.DeviceRequestAllocationResult.AdminAccess has no validation + + // field resourcev1.DeviceRequestAllocationResult.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) []resourcev1.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) []string { + return oldObj.BindingFailureConditions + }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *types.UID, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) *types.UID { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1.DeviceRequestAllocationResult.ConsumedCapacity has no validation + return errs +} + +// Validate_DeviceSubRequest validates an instance of DeviceSubRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceSubRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceSubRequest) (errs field.ErrorList) { + // field resourcev1.DeviceSubRequest.Name has no validation + + // field resourcev1.DeviceSubRequest.DeviceClassName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("deviceClassName"), &obj.DeviceClassName, safe.Field(oldObj, func(oldObj *resourcev1.DeviceSubRequest) *string { return &oldObj.DeviceClassName }), oldObj != nil)...) + + // field resourcev1.DeviceSubRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1.DeviceSubRequest) []resourcev1.DeviceSelector { return oldObj.Selectors }), oldObj != nil)...) + + // field resourcev1.DeviceSubRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1.DeviceSubRequest) *resourcev1.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1.DeviceSubRequest.Count has no validation + + // field resourcev1.DeviceSubRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1.DeviceSubRequest) []resourcev1.DeviceToleration { return oldObj.Tolerations }), oldObj != nil)...) + + // field resourcev1.DeviceSubRequest.Capacity has no validation + return errs +} + +// Validate_DeviceTaint validates an instance of DeviceTaint according +// to declarative validation rules in the API schema. +func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceTaint) (errs field.ErrorList) { + // field resourcev1.DeviceTaint.Key has no validation + // field resourcev1.DeviceTaint.Value has no validation + + // field resourcev1.DeviceTaint.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1.DeviceTaint) *resourcev1.DeviceTaintEffect { return &oldObj.Effect }), oldObj != nil)...) + + // field resourcev1.DeviceTaint.TimeAdded has no validation + return errs +} + +var symbolsForDeviceTaintEffect = sets.New(resourcev1.DeviceTaintEffectNoExecute, resourcev1.DeviceTaintEffectNoSchedule, resourcev1.DeviceTaintEffectNone) + +// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according +// to declarative validation rules in the API schema. +func Validate_DeviceTaintEffect(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceTaintEffect) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTaintEffect, nil)...) + + return errs +} + +// Validate_DeviceToleration validates an instance of DeviceToleration according +// to declarative validation rules in the API schema. +func Validate_DeviceToleration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceToleration) (errs field.ErrorList) { + // field resourcev1.DeviceToleration.Key + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LabelKey(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("key"), &obj.Key, safe.Field(oldObj, func(oldObj *resourcev1.DeviceToleration) *string { return &oldObj.Key }), oldObj != nil)...) + + // field resourcev1.DeviceToleration.Operator + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceTolerationOperator, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTolerationOperator(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("operator"), &obj.Operator, safe.Field(oldObj, func(oldObj *resourcev1.DeviceToleration) *resourcev1.DeviceTolerationOperator { + return &oldObj.Operator + }), oldObj != nil)...) + + // field resourcev1.DeviceToleration.Value has no validation + + // field resourcev1.DeviceToleration.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1.DeviceToleration) *resourcev1.DeviceTaintEffect { return &oldObj.Effect }), oldObj != nil)...) + + // field resourcev1.DeviceToleration.TolerationSeconds has no validation + return errs +} + +var symbolsForDeviceTolerationOperator = sets.New(resourcev1.DeviceTolerationOpEqual, resourcev1.DeviceTolerationOpExists) + +// Validate_DeviceTolerationOperator validates an instance of DeviceTolerationOperator according +// to declarative validation rules in the API schema. +func Validate_DeviceTolerationOperator(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceTolerationOperator) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTolerationOperator, nil)...) + + return errs +} + +// Validate_ExactDeviceRequest validates an instance of ExactDeviceRequest according +// to declarative validation rules in the API schema. +func Validate_ExactDeviceRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ExactDeviceRequest) (errs field.ErrorList) { + // field resourcev1.ExactDeviceRequest.DeviceClassName has no validation + + // field resourcev1.ExactDeviceRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1.ExactDeviceRequest) []resourcev1.DeviceSelector { return oldObj.Selectors }), oldObj != nil)...) + + // field resourcev1.ExactDeviceRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1.ExactDeviceRequest) *resourcev1.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1.ExactDeviceRequest.Count has no validation + // field resourcev1.ExactDeviceRequest.AdminAccess has no validation + + // field resourcev1.ExactDeviceRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1.ExactDeviceRequest) []resourcev1.DeviceToleration { return oldObj.Tolerations }), oldObj != nil)...) + + // field resourcev1.ExactDeviceRequest.Capacity has no validation + return errs +} + +// Validate_NetworkDeviceData validates an instance of NetworkDeviceData according +// to declarative validation rules in the API schema. +func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.NetworkDeviceData) (errs field.ErrorList) { + // field resourcev1.NetworkDeviceData.InterfaceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256)...) + return + }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) + + // field resourcev1.NetworkDeviceData.IPs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("ips"), obj.IPs, safe.Field(oldObj, func(oldObj *resourcev1.NetworkDeviceData) []string { return oldObj.IPs }), oldObj != nil)...) + + // field resourcev1.NetworkDeviceData.HardwareAddress + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128)...) + return + }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) + + return errs +} + +// Validate_OpaqueDeviceConfiguration validates an instance of OpaqueDeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_OpaqueDeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.OpaqueDeviceConfiguration) (errs field.ErrorList) { + // field resourcev1.OpaqueDeviceConfiguration.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1.OpaqueDeviceConfiguration) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1.OpaqueDeviceConfiguration.Parameters has no validation + return errs +} + +// Validate_ResourceClaim validates an instance of ResourceClaim according +// to declarative validation rules in the API schema. +func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaim) (errs field.ErrorList) { + // field resourcev1.ResourceClaim.TypeMeta has no validation + // field resourcev1.ResourceClaim.ObjectMeta has no validation + + // field resourcev1.ResourceClaim.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.Immutable(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaim) *resourcev1.ResourceClaimSpec { return &oldObj.Spec }), oldObj != nil)...) + + // field resourcev1.ResourceClaim.Status + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaim) *resourcev1.ResourceClaimStatus { return &oldObj.Status }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimList validates an instance of ResourceClaimList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimList) (errs field.ErrorList) { + // field resourcev1.ResourceClaimList.TypeMeta has no validation + // field resourcev1.ResourceClaimList.ListMeta has no validation + + // field resourcev1.ResourceClaimList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaim)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimList) []resourcev1.ResourceClaim { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimSpec validates an instance of ResourceClaimSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimSpec) (errs field.ErrorList) { + // field resourcev1.ResourceClaimSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClaim(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimSpec) *resourcev1.DeviceClaim { return &oldObj.Devices }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimStatus) (errs field.ErrorList) { + // field resourcev1.ResourceClaimStatus.Allocation + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.AllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdatePointer(ctx, op, fldPath, obj, oldObj, validate.NoModify); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) *resourcev1.AllocationResult { return oldObj.Allocation }), oldObj != nil)...) + + // field resourcev1.ResourceClaimStatus.ReservedFor + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceClaimConsumerReference, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 256); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.ResourceClaimConsumerReference, b resourcev1.ResourceClaimConsumerReference) bool { + return a.UID == b.UID + })...) + return + }(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) []resourcev1.ResourceClaimConsumerReference { + return oldObj.ReservedFor + }), oldObj != nil)...) + + // field resourcev1.ResourceClaimStatus.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.AllocatedDeviceStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.AllocatedDeviceStatus, b resourcev1.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.AllocatedDeviceStatus, b resourcev1.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) []resourcev1.AllocatedDeviceStatus { return oldObj.Devices }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplate validates an instance of ResourceClaimTemplate according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplate(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimTemplate) (errs field.ErrorList) { + // field resourcev1.ResourceClaimTemplate.TypeMeta has no validation + // field resourcev1.ResourceClaimTemplate.ObjectMeta has no validation + + // field resourcev1.ResourceClaimTemplate.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimTemplateSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimTemplateSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimTemplate) *resourcev1.ResourceClaimTemplateSpec { + return &oldObj.Spec + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateList validates an instance of ResourceClaimTemplateList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimTemplateList) (errs field.ErrorList) { + // field resourcev1.ResourceClaimTemplateList.TypeMeta has no validation + // field resourcev1.ResourceClaimTemplateList.ListMeta has no validation + + // field resourcev1.ResourceClaimTemplateList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceClaimTemplate, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaimTemplate)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimTemplateList) []resourcev1.ResourceClaimTemplate { + return oldObj.Items + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateSpec validates an instance of ResourceClaimTemplateSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimTemplateSpec) (errs field.ErrorList) { + // field resourcev1.ResourceClaimTemplateSpec.ObjectMeta has no validation + + // field resourcev1.ResourceClaimTemplateSpec.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimTemplateSpec) *resourcev1.ResourceClaimSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSlice validates an instance of ResourceSlice according +// to declarative validation rules in the API schema. +func Validate_ResourceSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceSlice) (errs field.ErrorList) { + // field resourcev1.ResourceSlice.TypeMeta has no validation + // field resourcev1.ResourceSlice.ObjectMeta has no validation + + // field resourcev1.ResourceSlice.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceSliceSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceSliceSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.ResourceSlice) *resourcev1.ResourceSliceSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceList validates an instance of ResourceSliceList according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceSliceList) (errs field.ErrorList) { + // field resourcev1.ResourceSliceList.TypeMeta has no validation + // field resourcev1.ResourceSliceList.ListMeta has no validation + + // field resourcev1.ResourceSliceList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceSlice, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceSlice)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.ResourceSliceList) []resourcev1.ResourceSlice { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceSpec validates an instance of ResourceSliceSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceSliceSpec) (errs field.ErrorList) { + // field resourcev1.ResourceSliceSpec.Driver has no validation + // field resourcev1.ResourceSliceSpec.Pool has no validation + // field resourcev1.ResourceSliceSpec.NodeName has no validation + // field resourcev1.ResourceSliceSpec.NodeSelector has no validation + // field resourcev1.ResourceSliceSpec.AllNodes has no validation + + // field resourcev1.ResourceSliceSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.Device, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Device)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.ResourceSliceSpec) []resourcev1.Device { return oldObj.Devices }), oldObj != nil)...) + + // field resourcev1.ResourceSliceSpec.PerDeviceNodeSelection has no validation + + // field resourcev1.ResourceSliceSpec.SharedCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1.CounterSet, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.CounterSet, b resourcev1.CounterSet) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.CounterSet, b resourcev1.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet)...) + return + }(fldPath.Child("sharedCounters"), obj.SharedCounters, safe.Field(oldObj, func(oldObj *resourcev1.ResourceSliceSpec) []resourcev1.CounterSet { return oldObj.SharedCounters }), oldObj != nil)...) + + return errs +} diff --git a/pkg/apis/resource/v1alpha3/zz_generated.conversion.go b/pkg/apis/resource/v1alpha3/zz_generated.conversion.go index aac8aca97cf82..e5c6ee21cf91f 100644 --- a/pkg/apis/resource/v1alpha3/zz_generated.conversion.go +++ b/pkg/apis/resource/v1alpha3/zz_generated.conversion.go @@ -98,6 +98,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.DeviceTaintRuleStatus)(nil), (*resource.DeviceTaintRuleStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(a.(*resourcev1alpha3.DeviceTaintRuleStatus), b.(*resource.DeviceTaintRuleStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*resource.DeviceTaintRuleStatus)(nil), (*resourcev1alpha3.DeviceTaintRuleStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(a.(*resource.DeviceTaintRuleStatus), b.(*resourcev1alpha3.DeviceTaintRuleStatus), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.DeviceTaintSelector)(nil), (*resource.DeviceTaintSelector)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(a.(*resourcev1alpha3.DeviceTaintSelector), b.(*resource.DeviceTaintSelector), scope) }); err != nil { @@ -182,6 +192,9 @@ func autoConvert_v1alpha3_DeviceTaintRule_To_resource_DeviceTaintRule(in *resour if err := Convert_v1alpha3_DeviceTaintRuleSpec_To_resource_DeviceTaintRuleSpec(&in.Spec, &out.Spec, s); err != nil { return err } + if err := Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(&in.Status, &out.Status, s); err != nil { + return err + } return nil } @@ -195,6 +208,9 @@ func autoConvert_resource_DeviceTaintRule_To_v1alpha3_DeviceTaintRule(in *resour if err := Convert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(&in.Spec, &out.Spec, s); err != nil { return err } + if err := Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(&in.Status, &out.Status, s); err != nil { + return err + } return nil } @@ -251,12 +267,30 @@ func Convert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(in *re return autoConvert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(in, out, s) } +func autoConvert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in *resourcev1alpha3.DeviceTaintRuleStatus, out *resource.DeviceTaintRuleStatus, s conversion.Scope) error { + out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus is an autogenerated conversion function. +func Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in *resourcev1alpha3.DeviceTaintRuleStatus, out *resource.DeviceTaintRuleStatus, s conversion.Scope) error { + return autoConvert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in, out, s) +} + +func autoConvert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in *resource.DeviceTaintRuleStatus, out *resourcev1alpha3.DeviceTaintRuleStatus, s conversion.Scope) error { + out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus is an autogenerated conversion function. +func Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in *resource.DeviceTaintRuleStatus, out *resourcev1alpha3.DeviceTaintRuleStatus, s conversion.Scope) error { + return autoConvert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in, out, s) +} + func autoConvert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(in *resourcev1alpha3.DeviceTaintSelector, out *resource.DeviceTaintSelector, s conversion.Scope) error { - out.DeviceClassName = (*string)(unsafe.Pointer(in.DeviceClassName)) out.Driver = (*string)(unsafe.Pointer(in.Driver)) out.Pool = (*string)(unsafe.Pointer(in.Pool)) out.Device = (*string)(unsafe.Pointer(in.Device)) - out.Selectors = *(*[]resource.DeviceSelector)(unsafe.Pointer(&in.Selectors)) return nil } @@ -266,11 +300,9 @@ func Convert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(in *re } func autoConvert_resource_DeviceTaintSelector_To_v1alpha3_DeviceTaintSelector(in *resource.DeviceTaintSelector, out *resourcev1alpha3.DeviceTaintSelector, s conversion.Scope) error { - out.DeviceClassName = (*string)(unsafe.Pointer(in.DeviceClassName)) out.Driver = (*string)(unsafe.Pointer(in.Driver)) out.Pool = (*string)(unsafe.Pointer(in.Pool)) out.Device = (*string)(unsafe.Pointer(in.Device)) - out.Selectors = *(*[]resourcev1alpha3.DeviceSelector)(unsafe.Pointer(&in.Selectors)) return nil } diff --git a/pkg/apis/resource/v1beta1/defaults.go b/pkg/apis/resource/v1beta1/defaults.go index 4e959c7d768dd..7d43132522d5e 100644 --- a/pkg/apis/resource/v1beta1/defaults.go +++ b/pkg/apis/resource/v1beta1/defaults.go @@ -35,6 +35,10 @@ func SetDefaults_DeviceRequest(obj *resourceapi.DeviceRequest) { if obj.DeviceClassName == "" { return } + // Declarative defaulting (+default) is not used for AllocationMode here because + // v1beta1 uses a bespoke union structure. Applying an unconditional default + // would make valid "FirstAvailable" requests (where this field must be empty) invalid. + // Therefore, we rely on this manual defaulting logic. if obj.AllocationMode == "" { obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount } diff --git a/pkg/apis/resource/v1beta1/doc.go b/pkg/apis/resource/v1beta1/doc.go index f014667f8da2e..f95eb5f3c9f0d 100644 --- a/pkg/apis/resource/v1beta1/doc.go +++ b/pkg/apis/resource/v1beta1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1beta1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1beta1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1beta1 // Package v1beta1 is the v1beta1 version of the resource API. package v1beta1 diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go new file mode 100644 index 0000000000000..f96f7aeaed4b4 --- /dev/null +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -0,0 +1,1860 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1beta1 + +import ( + context "context" + fmt "fmt" + + resourcev1beta1 "k8s.io/api/resource/v1beta1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type DeviceClass + scheme.AddValidationFunc((*resourcev1beta1.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.DeviceClass), safe.Cast[*resourcev1beta1.DeviceClass](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type DeviceClassList + scheme.AddValidationFunc((*resourcev1beta1.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.DeviceClassList), safe.Cast[*resourcev1beta1.DeviceClassList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaim + scheme.AddValidationFunc((*resourcev1beta1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/", "/status": + return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaim), safe.Cast[*resourcev1beta1.ResourceClaim](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimList + scheme.AddValidationFunc((*resourcev1beta1.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaimList), safe.Cast[*resourcev1beta1.ResourceClaimList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplate + scheme.AddValidationFunc((*resourcev1beta1.ResourceClaimTemplate)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplate(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaimTemplate), safe.Cast[*resourcev1beta1.ResourceClaimTemplate](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplateList + scheme.AddValidationFunc((*resourcev1beta1.ResourceClaimTemplateList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplateList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaimTemplateList), safe.Cast[*resourcev1beta1.ResourceClaimTemplateList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSlice + scheme.AddValidationFunc((*resourcev1beta1.ResourceSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSlice(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceSlice), safe.Cast[*resourcev1beta1.ResourceSlice](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSliceList + scheme.AddValidationFunc((*resourcev1beta1.ResourceSliceList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSliceList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceSliceList), safe.Cast[*resourcev1beta1.ResourceSliceList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_AllocatedDeviceStatus validates an instance of AllocatedDeviceStatus according +// to declarative validation rules in the API schema. +func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocatedDeviceStatus) (errs field.ErrorList) { + // field resourcev1beta1.AllocatedDeviceStatus.Driver has no validation + // field resourcev1beta1.AllocatedDeviceStatus.Pool has no validation + // field resourcev1beta1.AllocatedDeviceStatus.Device has no validation + + // field resourcev1beta1.AllocatedDeviceStatus.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1beta1.AllocatedDeviceStatus) *string { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1beta1.AllocatedDeviceStatus.Conditions has no validation + // field resourcev1beta1.AllocatedDeviceStatus.Data has no validation + + // field resourcev1beta1.AllocatedDeviceStatus.NetworkData + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.NetworkDeviceData, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_NetworkDeviceData(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("networkData"), obj.NetworkData, safe.Field(oldObj, func(oldObj *resourcev1beta1.AllocatedDeviceStatus) *resourcev1beta1.NetworkDeviceData { + return oldObj.NetworkData + }), oldObj != nil)...) + + return errs +} + +var symbolsForAllocationConfigSource = sets.New(resourcev1beta1.AllocationConfigSourceClaim, resourcev1beta1.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + +// Validate_AllocationResult validates an instance of AllocationResult according +// to declarative validation rules in the API schema. +func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationResult) (errs field.ErrorList) { + // field resourcev1beta1.AllocationResult.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.AllocationResult) *resourcev1beta1.DeviceAllocationResult { + return &oldObj.Devices + }), oldObj != nil)...) + + // field resourcev1beta1.AllocationResult.NodeSelector has no validation + // field resourcev1beta1.AllocationResult.AllocationTimestamp has no validation + return errs +} + +// Validate_BasicDevice validates an instance of BasicDevice according +// to declarative validation rules in the API schema. +func Validate_BasicDevice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.BasicDevice) (errs field.ErrorList) { + // field resourcev1beta1.BasicDevice.Attributes + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[resourcev1beta1.QualifiedName]resourcev1beta1.DeviceAttribute, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the map and call the value type's validation function + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute)...) + return + }(fldPath.Child("attributes"), obj.Attributes, safe.Field(oldObj, func(oldObj *resourcev1beta1.BasicDevice) map[resourcev1beta1.QualifiedName]resourcev1beta1.DeviceAttribute { + return oldObj.Attributes + }), oldObj != nil)...) + + // field resourcev1beta1.BasicDevice.Capacity has no validation + + // field resourcev1beta1.BasicDevice.ConsumesCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceCounterConsumption, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceCounterConsumption, b resourcev1beta1.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceCounterConsumption, b resourcev1beta1.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption)...) + return + }(fldPath.Child("consumesCounters"), obj.ConsumesCounters, safe.Field(oldObj, func(oldObj *resourcev1beta1.BasicDevice) []resourcev1beta1.DeviceCounterConsumption { + return oldObj.ConsumesCounters + }), oldObj != nil)...) + + // field resourcev1beta1.BasicDevice.NodeName has no validation + // field resourcev1beta1.BasicDevice.NodeSelector has no validation + // field resourcev1beta1.BasicDevice.AllNodes has no validation + + // field resourcev1beta1.BasicDevice.Taints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceTaint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceTaint)...) + return + }(fldPath.Child("taints"), obj.Taints, safe.Field(oldObj, func(oldObj *resourcev1beta1.BasicDevice) []resourcev1beta1.DeviceTaint { return oldObj.Taints }), oldObj != nil)...) + + // field resourcev1beta1.BasicDevice.BindsToNode has no validation + + // field resourcev1beta1.BasicDevice.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1beta1.BasicDevice) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1beta1.BasicDevice.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1beta1.BasicDevice) []string { return oldObj.BindingFailureConditions }), oldObj != nil)...) + + // field resourcev1beta1.BasicDevice.AllowMultipleAllocations has no validation + return errs +} + +// Validate_CounterSet validates an instance of CounterSet according +// to declarative validation rules in the API schema. +func Validate_CounterSet(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.CounterSet) (errs field.ErrorList) { + // field resourcev1beta1.CounterSet.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *resourcev1beta1.CounterSet) *string { return &oldObj.Name }), oldObj != nil)...) + + // field resourcev1beta1.CounterSet.Counters has no validation + return errs +} + +// Validate_Device validates an instance of Device according +// to declarative validation rules in the API schema. +func Validate_Device(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.Device) (errs field.ErrorList) { + // field resourcev1beta1.Device.Name has no validation + + // field resourcev1beta1.Device.Basic + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.BasicDevice, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_BasicDevice(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("basic"), obj.Basic, safe.Field(oldObj, func(oldObj *resourcev1beta1.Device) *resourcev1beta1.BasicDevice { return oldObj.Basic }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationConfiguration) (errs field.ErrorList) { + // field resourcev1beta1.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationConfigSource, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationConfiguration) *resourcev1beta1.AllocationConfigSource { + return &oldObj.Source + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAllocationConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAllocationConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationConfiguration) *resourcev1beta1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +var symbolsForDeviceAllocationMode = sets.New(resourcev1beta1.DeviceAllocationModeAll, resourcev1beta1.DeviceAllocationModeExactCount) + +// Validate_DeviceAllocationMode validates an instance of DeviceAllocationMode according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationMode(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationMode) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceAllocationMode, nil)...) + + return errs +} + +// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationResult) (errs field.ErrorList) { + // field resourcev1beta1.DeviceAllocationResult.Results + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceRequestAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequestAllocationResult)...) + return + }(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationResult) []resourcev1beta1.DeviceRequestAllocationResult { + return oldObj.Results + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAllocationResult.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceAllocationConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 64); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceAllocationConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationResult) []resourcev1beta1.DeviceAllocationConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + return errs +} + +var unionMembershipFor_k8s_io_api_resource_v1beta1_DeviceAttribute_ = validate.NewUnionMembership(validate.NewUnionMember("int"), validate.NewUnionMember("bool"), validate.NewUnionMember("string"), validate.NewUnionMember("version")) + +// Validate_DeviceAttribute validates an instance of DeviceAttribute according +// to declarative validation rules in the API schema. +func Validate_DeviceAttribute(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAttribute) (errs field.ErrorList) { + errs = append(errs, validate.Union(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_resource_v1beta1_DeviceAttribute_, func(obj *resourcev1beta1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.IntValue != nil + }, func(obj *resourcev1beta1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.BoolValue != nil + }, func(obj *resourcev1beta1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.StringValue != nil + }, func(obj *resourcev1beta1.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.VersionValue != nil + })...) + + // field resourcev1beta1.DeviceAttribute.IntValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int64, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("int"), obj.IntValue, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAttribute) *int64 { return oldObj.IntValue }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAttribute.BoolValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *bool, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bool"), obj.BoolValue, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAttribute) *bool { return oldObj.BoolValue }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAttribute.StringValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("string"), obj.StringValue, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAttribute) *string { return oldObj.StringValue }), oldObj != nil)...) + + // field resourcev1beta1.DeviceAttribute.VersionValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("version"), obj.VersionValue, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAttribute) *string { return oldObj.VersionValue }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaim validates an instance of DeviceClaim according +// to declarative validation rules in the API schema. +func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClaim) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClaim.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceRequest, b resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceRequest, b resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaim) []resourcev1beta1.DeviceRequest { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClaim.Constraints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceConstraint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceConstraint)...) + return + }(fldPath.Child("constraints"), obj.Constraints, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaim) []resourcev1beta1.DeviceConstraint { + return oldObj.Constraints + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClaim.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceClaimConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClaimConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaim) []resourcev1beta1.DeviceClaimConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaimConfiguration validates an instance of DeviceClaimConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClaimConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClaimConfiguration) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClaimConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaimConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClaimConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaimConfiguration) *resourcev1beta1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClass validates an instance of DeviceClass according +// to declarative validation rules in the API schema. +func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClass) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClass.TypeMeta has no validation + + // field resourcev1beta1.DeviceClass.ObjectMeta + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *v1.ObjectMeta, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + func() { // cohort name + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *v1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.OptionalValue); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *v1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.LongName)...) + }() + return + }(fldPath.Child("metadata"), &obj.ObjectMeta, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClass) *v1.ObjectMeta { return &oldObj.ObjectMeta }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClass.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClass) *resourcev1beta1.DeviceClassSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassConfiguration validates an instance of DeviceClassConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClassConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassConfiguration) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClassConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta1.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassConfiguration) *resourcev1beta1.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassList validates an instance of DeviceClassList according +// to declarative validation rules in the API schema. +func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassList) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClassList.TypeMeta has no validation + // field resourcev1beta1.DeviceClassList.ListMeta has no validation + + // field resourcev1beta1.DeviceClassList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceClass, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClass)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassList) []resourcev1beta1.DeviceClass { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according +// to declarative validation rules in the API schema. +func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassSpec) (errs field.ErrorList) { + // field resourcev1beta1.DeviceClassSpec.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassSpec) []resourcev1beta1.DeviceSelector { + return oldObj.Selectors + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClassSpec.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceClassConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClassConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassSpec) []resourcev1beta1.DeviceClassConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceClassSpec.ExtendedResourceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ExtendedResourceName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("extendedResourceName"), obj.ExtendedResourceName, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassSpec) *string { return oldObj.ExtendedResourceName }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConfiguration validates an instance of DeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceConfiguration) (errs field.ErrorList) { + // field resourcev1beta1.DeviceConfiguration.Opaque + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.OpaqueDeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_OpaqueDeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("opaque"), obj.Opaque, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceConfiguration) *resourcev1beta1.OpaqueDeviceConfiguration { + return oldObj.Opaque + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConstraint validates an instance of DeviceConstraint according +// to declarative validation rules in the API schema. +func Validate_DeviceConstraint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceConstraint) (errs field.ErrorList) { + // field resourcev1beta1.DeviceConstraint.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceConstraint) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta1.DeviceConstraint.MatchAttribute + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.FullyQualifiedName, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourceFullyQualifiedName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("matchAttribute"), obj.MatchAttribute, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceConstraint) *resourcev1beta1.FullyQualifiedName { + return oldObj.MatchAttribute + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceConstraint.DistinctAttribute has no validation + return errs +} + +// Validate_DeviceCounterConsumption validates an instance of DeviceCounterConsumption according +// to declarative validation rules in the API schema. +func Validate_DeviceCounterConsumption(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceCounterConsumption) (errs field.ErrorList) { + // field resourcev1beta1.DeviceCounterConsumption.CounterSet + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("counterSet"), &obj.CounterSet, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceCounterConsumption) *string { return &oldObj.CounterSet }), oldObj != nil)...) + + // field resourcev1beta1.DeviceCounterConsumption.Counters has no validation + return errs +} + +// Validate_DeviceRequest validates an instance of DeviceRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceRequest) (errs field.ErrorList) { + // field resourcev1beta1.DeviceRequest.Name has no validation + // field resourcev1beta1.DeviceRequest.DeviceClassName has no validation + + // field resourcev1beta1.DeviceRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequest) []resourcev1beta1.DeviceSelector { return oldObj.Selectors }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequest) *resourcev1beta1.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequest.Count has no validation + // field resourcev1beta1.DeviceRequest.AdminAccess has no validation + + // field resourcev1beta1.DeviceRequest.FirstAvailable + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceSubRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceSubRequest, b resourcev1beta1.DeviceSubRequest) bool { + return a.Name == b.Name + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceSubRequest, b resourcev1beta1.DeviceSubRequest) bool { + return a.Name == b.Name + }, validate.SemanticDeepEqual, Validate_DeviceSubRequest)...) + return + }(fldPath.Child("firstAvailable"), obj.FirstAvailable, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequest) []resourcev1beta1.DeviceSubRequest { + return oldObj.FirstAvailable + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequest) []resourcev1beta1.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequest.Capacity has no validation + return errs +} + +// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceRequestAllocationResult) (errs field.ErrorList) { + // field resourcev1beta1.DeviceRequestAllocationResult.Request has no validation + + // field resourcev1beta1.DeviceRequestAllocationResult.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.Pool + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) *string { return &oldObj.Pool }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.Device has no validation + // field resourcev1beta1.DeviceRequestAllocationResult.AdminAccess has no validation + + // field resourcev1beta1.DeviceRequestAllocationResult.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) []resourcev1beta1.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) []string { + return oldObj.BindingFailureConditions + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *types.UID, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) *types.UID { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1beta1.DeviceRequestAllocationResult.ConsumedCapacity has no validation + return errs +} + +// Validate_DeviceSubRequest validates an instance of DeviceSubRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceSubRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceSubRequest) (errs field.ErrorList) { + // field resourcev1beta1.DeviceSubRequest.Name has no validation + + // field resourcev1beta1.DeviceSubRequest.DeviceClassName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("deviceClassName"), &obj.DeviceClassName, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceSubRequest) *string { return &oldObj.DeviceClassName }), oldObj != nil)...) + + // field resourcev1beta1.DeviceSubRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceSubRequest) []resourcev1beta1.DeviceSelector { + return oldObj.Selectors + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceSubRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceSubRequest) *resourcev1beta1.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceSubRequest.Count has no validation + + // field resourcev1beta1.DeviceSubRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceSubRequest) []resourcev1beta1.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceSubRequest.Capacity has no validation + return errs +} + +// Validate_DeviceTaint validates an instance of DeviceTaint according +// to declarative validation rules in the API schema. +func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTaint) (errs field.ErrorList) { + // field resourcev1beta1.DeviceTaint.Key has no validation + // field resourcev1beta1.DeviceTaint.Value has no validation + + // field resourcev1beta1.DeviceTaint.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceTaint) *resourcev1beta1.DeviceTaintEffect { return &oldObj.Effect }), oldObj != nil)...) + + // field resourcev1beta1.DeviceTaint.TimeAdded has no validation + return errs +} + +var symbolsForDeviceTaintEffect = sets.New(resourcev1beta1.DeviceTaintEffectNoExecute, resourcev1beta1.DeviceTaintEffectNoSchedule, resourcev1beta1.DeviceTaintEffectNone) + +// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according +// to declarative validation rules in the API schema. +func Validate_DeviceTaintEffect(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTaintEffect) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTaintEffect, nil)...) + + return errs +} + +// Validate_DeviceToleration validates an instance of DeviceToleration according +// to declarative validation rules in the API schema. +func Validate_DeviceToleration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceToleration) (errs field.ErrorList) { + // field resourcev1beta1.DeviceToleration.Key + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LabelKey(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("key"), &obj.Key, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceToleration) *string { return &oldObj.Key }), oldObj != nil)...) + + // field resourcev1beta1.DeviceToleration.Operator + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTolerationOperator, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTolerationOperator(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("operator"), &obj.Operator, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceToleration) *resourcev1beta1.DeviceTolerationOperator { + return &oldObj.Operator + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceToleration.Value has no validation + + // field resourcev1beta1.DeviceToleration.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceToleration) *resourcev1beta1.DeviceTaintEffect { + return &oldObj.Effect + }), oldObj != nil)...) + + // field resourcev1beta1.DeviceToleration.TolerationSeconds has no validation + return errs +} + +var symbolsForDeviceTolerationOperator = sets.New(resourcev1beta1.DeviceTolerationOpEqual, resourcev1beta1.DeviceTolerationOpExists) + +// Validate_DeviceTolerationOperator validates an instance of DeviceTolerationOperator according +// to declarative validation rules in the API schema. +func Validate_DeviceTolerationOperator(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceTolerationOperator) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTolerationOperator, nil)...) + + return errs +} + +// Validate_NetworkDeviceData validates an instance of NetworkDeviceData according +// to declarative validation rules in the API schema. +func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.NetworkDeviceData) (errs field.ErrorList) { + // field resourcev1beta1.NetworkDeviceData.InterfaceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256)...) + return + }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1beta1.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) + + // field resourcev1beta1.NetworkDeviceData.IPs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("ips"), obj.IPs, safe.Field(oldObj, func(oldObj *resourcev1beta1.NetworkDeviceData) []string { return oldObj.IPs }), oldObj != nil)...) + + // field resourcev1beta1.NetworkDeviceData.HardwareAddress + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128)...) + return + }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1beta1.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) + + return errs +} + +// Validate_OpaqueDeviceConfiguration validates an instance of OpaqueDeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_OpaqueDeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.OpaqueDeviceConfiguration) (errs field.ErrorList) { + // field resourcev1beta1.OpaqueDeviceConfiguration.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1beta1.OpaqueDeviceConfiguration) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1beta1.OpaqueDeviceConfiguration.Parameters has no validation + return errs +} + +// Validate_ResourceClaim validates an instance of ResourceClaim according +// to declarative validation rules in the API schema. +func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaim) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaim.TypeMeta has no validation + // field resourcev1beta1.ResourceClaim.ObjectMeta has no validation + + // field resourcev1beta1.ResourceClaim.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.Immutable(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaim) *resourcev1beta1.ResourceClaimSpec { return &oldObj.Spec }), oldObj != nil)...) + + // field resourcev1beta1.ResourceClaim.Status + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaim) *resourcev1beta1.ResourceClaimStatus { + return &oldObj.Status + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimList validates an instance of ResourceClaimList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimList) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimList.TypeMeta has no validation + // field resourcev1beta1.ResourceClaimList.ListMeta has no validation + + // field resourcev1beta1.ResourceClaimList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaim)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimList) []resourcev1beta1.ResourceClaim { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimSpec validates an instance of ResourceClaimSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimSpec) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClaim(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimSpec) *resourcev1beta1.DeviceClaim { return &oldObj.Devices }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimStatus) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimStatus.Allocation + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdatePointer(ctx, op, fldPath, obj, oldObj, validate.NoModify); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) *resourcev1beta1.AllocationResult { + return oldObj.Allocation + }), oldObj != nil)...) + + // field resourcev1beta1.ResourceClaimStatus.ReservedFor + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceClaimConsumerReference, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 256); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.ResourceClaimConsumerReference, b resourcev1beta1.ResourceClaimConsumerReference) bool { + return a.UID == b.UID + })...) + return + }(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) []resourcev1beta1.ResourceClaimConsumerReference { + return oldObj.ReservedFor + }), oldObj != nil)...) + + // field resourcev1beta1.ResourceClaimStatus.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.AllocatedDeviceStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.AllocatedDeviceStatus, b resourcev1beta1.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.AllocatedDeviceStatus, b resourcev1beta1.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) []resourcev1beta1.AllocatedDeviceStatus { + return oldObj.Devices + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplate validates an instance of ResourceClaimTemplate according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplate(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimTemplate) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimTemplate.TypeMeta has no validation + // field resourcev1beta1.ResourceClaimTemplate.ObjectMeta has no validation + + // field resourcev1beta1.ResourceClaimTemplate.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimTemplateSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimTemplateSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimTemplate) *resourcev1beta1.ResourceClaimTemplateSpec { + return &oldObj.Spec + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateList validates an instance of ResourceClaimTemplateList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimTemplateList) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimTemplateList.TypeMeta has no validation + // field resourcev1beta1.ResourceClaimTemplateList.ListMeta has no validation + + // field resourcev1beta1.ResourceClaimTemplateList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceClaimTemplate, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaimTemplate)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimTemplateList) []resourcev1beta1.ResourceClaimTemplate { + return oldObj.Items + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateSpec validates an instance of ResourceClaimTemplateSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimTemplateSpec) (errs field.ErrorList) { + // field resourcev1beta1.ResourceClaimTemplateSpec.ObjectMeta has no validation + + // field resourcev1beta1.ResourceClaimTemplateSpec.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimTemplateSpec) *resourcev1beta1.ResourceClaimSpec { + return &oldObj.Spec + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSlice validates an instance of ResourceSlice according +// to declarative validation rules in the API schema. +func Validate_ResourceSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceSlice) (errs field.ErrorList) { + // field resourcev1beta1.ResourceSlice.TypeMeta has no validation + // field resourcev1beta1.ResourceSlice.ObjectMeta has no validation + + // field resourcev1beta1.ResourceSlice.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceSliceSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceSliceSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceSlice) *resourcev1beta1.ResourceSliceSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceList validates an instance of ResourceSliceList according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceSliceList) (errs field.ErrorList) { + // field resourcev1beta1.ResourceSliceList.TypeMeta has no validation + // field resourcev1beta1.ResourceSliceList.ListMeta has no validation + + // field resourcev1beta1.ResourceSliceList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceSlice, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceSlice)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceSliceList) []resourcev1beta1.ResourceSlice { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceSpec validates an instance of ResourceSliceSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceSliceSpec) (errs field.ErrorList) { + // field resourcev1beta1.ResourceSliceSpec.Driver has no validation + // field resourcev1beta1.ResourceSliceSpec.Pool has no validation + // field resourcev1beta1.ResourceSliceSpec.NodeName has no validation + // field resourcev1beta1.ResourceSliceSpec.NodeSelector has no validation + // field resourcev1beta1.ResourceSliceSpec.AllNodes has no validation + + // field resourcev1beta1.ResourceSliceSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.Device, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Device)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceSliceSpec) []resourcev1beta1.Device { return oldObj.Devices }), oldObj != nil)...) + + // field resourcev1beta1.ResourceSliceSpec.PerDeviceNodeSelection has no validation + + // field resourcev1beta1.ResourceSliceSpec.SharedCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta1.CounterSet, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.CounterSet, b resourcev1beta1.CounterSet) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.CounterSet, b resourcev1beta1.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet)...) + return + }(fldPath.Child("sharedCounters"), obj.SharedCounters, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceSliceSpec) []resourcev1beta1.CounterSet { + return oldObj.SharedCounters + }), oldObj != nil)...) + + return errs +} diff --git a/pkg/apis/resource/v1beta2/doc.go b/pkg/apis/resource/v1beta2/doc.go index beea4f266b95f..ca447ac715443 100644 --- a/pkg/apis/resource/v1beta2/doc.go +++ b/pkg/apis/resource/v1beta2/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1beta2 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1beta2 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1beta2 // Package v1beta2 is the v1beta2 version of the resource API. package v1beta2 diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go new file mode 100644 index 0000000000000..2960f5fa78b6d --- /dev/null +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -0,0 +1,1862 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1beta2 + +import ( + context "context" + fmt "fmt" + + resourcev1beta2 "k8s.io/api/resource/v1beta2" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type DeviceClass + scheme.AddValidationFunc((*resourcev1beta2.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.DeviceClass), safe.Cast[*resourcev1beta2.DeviceClass](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type DeviceClassList + scheme.AddValidationFunc((*resourcev1beta2.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.DeviceClassList), safe.Cast[*resourcev1beta2.DeviceClassList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaim + scheme.AddValidationFunc((*resourcev1beta2.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/", "/status": + return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaim), safe.Cast[*resourcev1beta2.ResourceClaim](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimList + scheme.AddValidationFunc((*resourcev1beta2.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaimList), safe.Cast[*resourcev1beta2.ResourceClaimList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplate + scheme.AddValidationFunc((*resourcev1beta2.ResourceClaimTemplate)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplate(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaimTemplate), safe.Cast[*resourcev1beta2.ResourceClaimTemplate](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceClaimTemplateList + scheme.AddValidationFunc((*resourcev1beta2.ResourceClaimTemplateList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceClaimTemplateList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaimTemplateList), safe.Cast[*resourcev1beta2.ResourceClaimTemplateList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSlice + scheme.AddValidationFunc((*resourcev1beta2.ResourceSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSlice(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceSlice), safe.Cast[*resourcev1beta2.ResourceSlice](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type ResourceSliceList + scheme.AddValidationFunc((*resourcev1beta2.ResourceSliceList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_ResourceSliceList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceSliceList), safe.Cast[*resourcev1beta2.ResourceSliceList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_AllocatedDeviceStatus validates an instance of AllocatedDeviceStatus according +// to declarative validation rules in the API schema. +func Validate_AllocatedDeviceStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocatedDeviceStatus) (errs field.ErrorList) { + // field resourcev1beta2.AllocatedDeviceStatus.Driver has no validation + // field resourcev1beta2.AllocatedDeviceStatus.Pool has no validation + // field resourcev1beta2.AllocatedDeviceStatus.Device has no validation + + // field resourcev1beta2.AllocatedDeviceStatus.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1beta2.AllocatedDeviceStatus) *string { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1beta2.AllocatedDeviceStatus.Conditions has no validation + // field resourcev1beta2.AllocatedDeviceStatus.Data has no validation + + // field resourcev1beta2.AllocatedDeviceStatus.NetworkData + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.NetworkDeviceData, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_NetworkDeviceData(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("networkData"), obj.NetworkData, safe.Field(oldObj, func(oldObj *resourcev1beta2.AllocatedDeviceStatus) *resourcev1beta2.NetworkDeviceData { + return oldObj.NetworkData + }), oldObj != nil)...) + + return errs +} + +var symbolsForAllocationConfigSource = sets.New(resourcev1beta2.AllocationConfigSourceClaim, resourcev1beta2.AllocationConfigSourceClass) + +// Validate_AllocationConfigSource validates an instance of AllocationConfigSource according +// to declarative validation rules in the API schema. +func Validate_AllocationConfigSource(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationConfigSource) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForAllocationConfigSource, nil)...) + + return errs +} + +// Validate_AllocationResult validates an instance of AllocationResult according +// to declarative validation rules in the API schema. +func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationResult) (errs field.ErrorList) { + // field resourcev1beta2.AllocationResult.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.AllocationResult) *resourcev1beta2.DeviceAllocationResult { + return &oldObj.Devices + }), oldObj != nil)...) + + // field resourcev1beta2.AllocationResult.NodeSelector has no validation + // field resourcev1beta2.AllocationResult.AllocationTimestamp has no validation + return errs +} + +// Validate_CounterSet validates an instance of CounterSet according +// to declarative validation rules in the API schema. +func Validate_CounterSet(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.CounterSet) (errs field.ErrorList) { + // field resourcev1beta2.CounterSet.Name + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *resourcev1beta2.CounterSet) *string { return &oldObj.Name }), oldObj != nil)...) + + // field resourcev1beta2.CounterSet.Counters has no validation + return errs +} + +// Validate_Device validates an instance of Device according +// to declarative validation rules in the API schema. +func Validate_Device(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.Device) (errs field.ErrorList) { + // field resourcev1beta2.Device.Name has no validation + + // field resourcev1beta2.Device.Attributes + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[resourcev1beta2.QualifiedName]resourcev1beta2.DeviceAttribute, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the map and call the value type's validation function + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute)...) + return + }(fldPath.Child("attributes"), obj.Attributes, safe.Field(oldObj, func(oldObj *resourcev1beta2.Device) map[resourcev1beta2.QualifiedName]resourcev1beta2.DeviceAttribute { + return oldObj.Attributes + }), oldObj != nil)...) + + // field resourcev1beta2.Device.Capacity has no validation + + // field resourcev1beta2.Device.ConsumesCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceCounterConsumption, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceCounterConsumption, b resourcev1beta2.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceCounterConsumption, b resourcev1beta2.DeviceCounterConsumption) bool { + return a.CounterSet == b.CounterSet + }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption)...) + return + }(fldPath.Child("consumesCounters"), obj.ConsumesCounters, safe.Field(oldObj, func(oldObj *resourcev1beta2.Device) []resourcev1beta2.DeviceCounterConsumption { + return oldObj.ConsumesCounters + }), oldObj != nil)...) + + // field resourcev1beta2.Device.NodeName has no validation + // field resourcev1beta2.Device.NodeSelector has no validation + // field resourcev1beta2.Device.AllNodes has no validation + + // field resourcev1beta2.Device.Taints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceTaint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceTaint)...) + return + }(fldPath.Child("taints"), obj.Taints, safe.Field(oldObj, func(oldObj *resourcev1beta2.Device) []resourcev1beta2.DeviceTaint { return oldObj.Taints }), oldObj != nil)...) + + // field resourcev1beta2.Device.BindsToNode has no validation + + // field resourcev1beta2.Device.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1beta2.Device) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1beta2.Device.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1beta2.Device) []string { return oldObj.BindingFailureConditions }), oldObj != nil)...) + + // field resourcev1beta2.Device.AllowMultipleAllocations has no validation + return errs +} + +// Validate_DeviceAllocationConfiguration validates an instance of DeviceAllocationConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationConfiguration) (errs field.ErrorList) { + // field resourcev1beta2.DeviceAllocationConfiguration.Source + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationConfigSource, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationConfigSource(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("source"), &obj.Source, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationConfiguration) *resourcev1beta2.AllocationConfigSource { + return &oldObj.Source + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAllocationConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAllocationConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta2.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationConfiguration) *resourcev1beta2.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +var symbolsForDeviceAllocationMode = sets.New(resourcev1beta2.DeviceAllocationModeAll, resourcev1beta2.DeviceAllocationModeExactCount) + +// Validate_DeviceAllocationMode validates an instance of DeviceAllocationMode according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationMode(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationMode) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceAllocationMode, nil)...) + + return errs +} + +// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationResult) (errs field.ErrorList) { + // field resourcev1beta2.DeviceAllocationResult.Results + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceRequestAllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequestAllocationResult)...) + return + }(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationResult) []resourcev1beta2.DeviceRequestAllocationResult { + return oldObj.Results + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAllocationResult.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceAllocationConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 64); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceAllocationConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationResult) []resourcev1beta2.DeviceAllocationConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + return errs +} + +var unionMembershipFor_k8s_io_api_resource_v1beta2_DeviceAttribute_ = validate.NewUnionMembership(validate.NewUnionMember("int"), validate.NewUnionMember("bool"), validate.NewUnionMember("string"), validate.NewUnionMember("version")) + +// Validate_DeviceAttribute validates an instance of DeviceAttribute according +// to declarative validation rules in the API schema. +func Validate_DeviceAttribute(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAttribute) (errs field.ErrorList) { + errs = append(errs, validate.Union(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_api_resource_v1beta2_DeviceAttribute_, func(obj *resourcev1beta2.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.IntValue != nil + }, func(obj *resourcev1beta2.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.BoolValue != nil + }, func(obj *resourcev1beta2.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.StringValue != nil + }, func(obj *resourcev1beta2.DeviceAttribute) bool { + if obj == nil { + return false + } + return obj.VersionValue != nil + })...) + + // field resourcev1beta2.DeviceAttribute.IntValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int64, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("int"), obj.IntValue, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAttribute) *int64 { return oldObj.IntValue }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAttribute.BoolValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *bool, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bool"), obj.BoolValue, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAttribute) *bool { return oldObj.BoolValue }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAttribute.StringValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("string"), obj.StringValue, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAttribute) *string { return oldObj.StringValue }), oldObj != nil)...) + + // field resourcev1beta2.DeviceAttribute.VersionValue + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("version"), obj.VersionValue, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAttribute) *string { return oldObj.VersionValue }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaim validates an instance of DeviceClaim according +// to declarative validation rules in the API schema. +func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClaim) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClaim.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceRequest, b resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceRequest, b resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaim) []resourcev1beta2.DeviceRequest { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClaim.Constraints + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceConstraint, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceConstraint)...) + return + }(fldPath.Child("constraints"), obj.Constraints, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaim) []resourcev1beta2.DeviceConstraint { + return oldObj.Constraints + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClaim.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceClaimConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClaimConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaim) []resourcev1beta2.DeviceClaimConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClaimConfiguration validates an instance of DeviceClaimConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClaimConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClaimConfiguration) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClaimConfiguration.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaimConfiguration) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClaimConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta2.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaimConfiguration) *resourcev1beta2.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClass validates an instance of DeviceClass according +// to declarative validation rules in the API schema. +func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClass) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClass.TypeMeta has no validation + + // field resourcev1beta2.DeviceClass.ObjectMeta + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *v1.ObjectMeta, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + func() { // cohort name + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *v1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.OptionalValue); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "name", func(o *v1.ObjectMeta) *string { return &o.Name }, validate.DirectEqualPtr, validate.LongName)...) + }() + return + }(fldPath.Child("metadata"), &obj.ObjectMeta, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClass) *v1.ObjectMeta { return &oldObj.ObjectMeta }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClass.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClass) *resourcev1beta2.DeviceClassSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassConfiguration validates an instance of DeviceClassConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceClassConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassConfiguration) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClassConfiguration.DeviceConfiguration + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("resourcev1beta2.DeviceConfiguration") }), &obj.DeviceConfiguration, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassConfiguration) *resourcev1beta2.DeviceConfiguration { + return &oldObj.DeviceConfiguration + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassList validates an instance of DeviceClassList according +// to declarative validation rules in the API schema. +func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassList) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClassList.TypeMeta has no validation + // field resourcev1beta2.DeviceClassList.ListMeta has no validation + + // field resourcev1beta2.DeviceClassList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceClass, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClass)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassList) []resourcev1beta2.DeviceClass { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according +// to declarative validation rules in the API schema. +func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassSpec) (errs field.ErrorList) { + // field resourcev1beta2.DeviceClassSpec.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassSpec) []resourcev1beta2.DeviceSelector { + return oldObj.Selectors + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClassSpec.Config + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceClassConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceClassConfiguration)...) + return + }(fldPath.Child("config"), obj.Config, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassSpec) []resourcev1beta2.DeviceClassConfiguration { + return oldObj.Config + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceClassSpec.ExtendedResourceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ExtendedResourceName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("extendedResourceName"), obj.ExtendedResourceName, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassSpec) *string { return oldObj.ExtendedResourceName }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConfiguration validates an instance of DeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_DeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceConfiguration) (errs field.ErrorList) { + // field resourcev1beta2.DeviceConfiguration.Opaque + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.OpaqueDeviceConfiguration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_OpaqueDeviceConfiguration(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("opaque"), obj.Opaque, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceConfiguration) *resourcev1beta2.OpaqueDeviceConfiguration { + return oldObj.Opaque + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceConstraint validates an instance of DeviceConstraint according +// to declarative validation rules in the API schema. +func Validate_DeviceConstraint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceConstraint) (errs field.ErrorList) { + // field resourcev1beta2.DeviceConstraint.Requests + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceConstraint) []string { return oldObj.Requests }), oldObj != nil)...) + + // field resourcev1beta2.DeviceConstraint.MatchAttribute + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.FullyQualifiedName, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourceFullyQualifiedName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("matchAttribute"), obj.MatchAttribute, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceConstraint) *resourcev1beta2.FullyQualifiedName { + return oldObj.MatchAttribute + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceConstraint.DistinctAttribute has no validation + return errs +} + +// Validate_DeviceCounterConsumption validates an instance of DeviceCounterConsumption according +// to declarative validation rules in the API schema. +func Validate_DeviceCounterConsumption(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceCounterConsumption) (errs field.ErrorList) { + // field resourcev1beta2.DeviceCounterConsumption.CounterSet + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ShortName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("counterSet"), &obj.CounterSet, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceCounterConsumption) *string { return &oldObj.CounterSet }), oldObj != nil)...) + + // field resourcev1beta2.DeviceCounterConsumption.Counters has no validation + return errs +} + +// Validate_DeviceRequest validates an instance of DeviceRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceRequest) (errs field.ErrorList) { + // field resourcev1beta2.DeviceRequest.Name has no validation + + // field resourcev1beta2.DeviceRequest.Exactly + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ExactDeviceRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ExactDeviceRequest(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("exactly"), obj.Exactly, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequest) *resourcev1beta2.ExactDeviceRequest { return oldObj.Exactly }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequest.FirstAvailable + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceSubRequest, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceSubRequest, b resourcev1beta2.DeviceSubRequest) bool { + return a.Name == b.Name + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceSubRequest, b resourcev1beta2.DeviceSubRequest) bool { + return a.Name == b.Name + }, validate.SemanticDeepEqual, Validate_DeviceSubRequest)...) + return + }(fldPath.Child("firstAvailable"), obj.FirstAvailable, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequest) []resourcev1beta2.DeviceSubRequest { + return oldObj.FirstAvailable + }), oldObj != nil)...) + + return errs +} + +// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according +// to declarative validation rules in the API schema. +func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceRequestAllocationResult) (errs field.ErrorList) { + // field resourcev1beta2.DeviceRequestAllocationResult.Request has no validation + + // field resourcev1beta2.DeviceRequestAllocationResult.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.Pool + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) *string { return &oldObj.Pool }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.Device has no validation + // field resourcev1beta2.DeviceRequestAllocationResult.AdminAccess has no validation + + // field resourcev1beta2.DeviceRequestAllocationResult.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) []resourcev1beta2.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.BindingConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingConditions"), obj.BindingConditions, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) []string { return oldObj.BindingConditions }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.BindingFailureConditions + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 4); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("bindingFailureConditions"), obj.BindingFailureConditions, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) []string { + return oldObj.BindingFailureConditions + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.ShareID + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *types.UID, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.UUID(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("shareID"), obj.ShareID, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) *types.UID { return oldObj.ShareID }), oldObj != nil)...) + + // field resourcev1beta2.DeviceRequestAllocationResult.ConsumedCapacity has no validation + return errs +} + +// Validate_DeviceSubRequest validates an instance of DeviceSubRequest according +// to declarative validation rules in the API schema. +func Validate_DeviceSubRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceSubRequest) (errs field.ErrorList) { + // field resourcev1beta2.DeviceSubRequest.Name has no validation + + // field resourcev1beta2.DeviceSubRequest.DeviceClassName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongName(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("deviceClassName"), &obj.DeviceClassName, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceSubRequest) *string { return &oldObj.DeviceClassName }), oldObj != nil)...) + + // field resourcev1beta2.DeviceSubRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceSubRequest) []resourcev1beta2.DeviceSelector { + return oldObj.Selectors + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceSubRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceSubRequest) *resourcev1beta2.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceSubRequest.Count has no validation + + // field resourcev1beta2.DeviceSubRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceSubRequest) []resourcev1beta2.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceSubRequest.Capacity has no validation + return errs +} + +// Validate_DeviceTaint validates an instance of DeviceTaint according +// to declarative validation rules in the API schema. +func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTaint) (errs field.ErrorList) { + // field resourcev1beta2.DeviceTaint.Key has no validation + // field resourcev1beta2.DeviceTaint.Value has no validation + + // field resourcev1beta2.DeviceTaint.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceTaint) *resourcev1beta2.DeviceTaintEffect { return &oldObj.Effect }), oldObj != nil)...) + + // field resourcev1beta2.DeviceTaint.TimeAdded has no validation + return errs +} + +var symbolsForDeviceTaintEffect = sets.New(resourcev1beta2.DeviceTaintEffectNoExecute, resourcev1beta2.DeviceTaintEffectNoSchedule, resourcev1beta2.DeviceTaintEffectNone) + +// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according +// to declarative validation rules in the API schema. +func Validate_DeviceTaintEffect(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTaintEffect) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTaintEffect, nil)...) + + return errs +} + +// Validate_DeviceToleration validates an instance of DeviceToleration according +// to declarative validation rules in the API schema. +func Validate_DeviceToleration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceToleration) (errs field.ErrorList) { + // field resourcev1beta2.DeviceToleration.Key + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LabelKey(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("key"), &obj.Key, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceToleration) *string { return &oldObj.Key }), oldObj != nil)...) + + // field resourcev1beta2.DeviceToleration.Operator + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTolerationOperator, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTolerationOperator(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("operator"), &obj.Operator, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceToleration) *resourcev1beta2.DeviceTolerationOperator { + return &oldObj.Operator + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceToleration.Value has no validation + + // field resourcev1beta2.DeviceToleration.Effect + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTaintEffect, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceTaintEffect(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("effect"), &obj.Effect, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceToleration) *resourcev1beta2.DeviceTaintEffect { + return &oldObj.Effect + }), oldObj != nil)...) + + // field resourcev1beta2.DeviceToleration.TolerationSeconds has no validation + return errs +} + +var symbolsForDeviceTolerationOperator = sets.New(resourcev1beta2.DeviceTolerationOpEqual, resourcev1beta2.DeviceTolerationOpExists) + +// Validate_DeviceTolerationOperator validates an instance of DeviceTolerationOperator according +// to declarative validation rules in the API schema. +func Validate_DeviceTolerationOperator(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceTolerationOperator) (errs field.ErrorList) { + errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForDeviceTolerationOperator, nil)...) + + return errs +} + +// Validate_ExactDeviceRequest validates an instance of ExactDeviceRequest according +// to declarative validation rules in the API schema. +func Validate_ExactDeviceRequest(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ExactDeviceRequest) (errs field.ErrorList) { + // field resourcev1beta2.ExactDeviceRequest.DeviceClassName has no validation + + // field resourcev1beta2.ExactDeviceRequest.Selectors + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceSelector, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta2.ExactDeviceRequest) []resourcev1beta2.DeviceSelector { + return oldObj.Selectors + }), oldObj != nil)...) + + // field resourcev1beta2.ExactDeviceRequest.AllocationMode + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationMode, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeviceAllocationMode(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocationMode"), &obj.AllocationMode, safe.Field(oldObj, func(oldObj *resourcev1beta2.ExactDeviceRequest) *resourcev1beta2.DeviceAllocationMode { + return &oldObj.AllocationMode + }), oldObj != nil)...) + + // field resourcev1beta2.ExactDeviceRequest.Count has no validation + // field resourcev1beta2.ExactDeviceRequest.AdminAccess has no validation + + // field resourcev1beta2.ExactDeviceRequest.Tolerations + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceToleration, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceToleration)...) + return + }(fldPath.Child("tolerations"), obj.Tolerations, safe.Field(oldObj, func(oldObj *resourcev1beta2.ExactDeviceRequest) []resourcev1beta2.DeviceToleration { + return oldObj.Tolerations + }), oldObj != nil)...) + + // field resourcev1beta2.ExactDeviceRequest.Capacity has no validation + return errs +} + +// Validate_NetworkDeviceData validates an instance of NetworkDeviceData according +// to declarative validation rules in the API schema. +func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.NetworkDeviceData) (errs field.ErrorList) { + // field resourcev1beta2.NetworkDeviceData.InterfaceName + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256)...) + return + }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1beta2.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) + + // field resourcev1beta2.NetworkDeviceData.IPs + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with set semantics require unique values + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...) + return + }(fldPath.Child("ips"), obj.IPs, safe.Field(oldObj, func(oldObj *resourcev1beta2.NetworkDeviceData) []string { return oldObj.IPs }), oldObj != nil)...) + + // field resourcev1beta2.NetworkDeviceData.HardwareAddress + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128)...) + return + }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1beta2.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) + + return errs +} + +// Validate_OpaqueDeviceConfiguration validates an instance of OpaqueDeviceConfiguration according +// to declarative validation rules in the API schema. +func Validate_OpaqueDeviceConfiguration(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.OpaqueDeviceConfiguration) (errs field.ErrorList) { + // field resourcev1beta2.OpaqueDeviceConfiguration.Driver + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + errs = append(errs, validate.LongNameCaseless(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("driver"), &obj.Driver, safe.Field(oldObj, func(oldObj *resourcev1beta2.OpaqueDeviceConfiguration) *string { return &oldObj.Driver }), oldObj != nil)...) + + // field resourcev1beta2.OpaqueDeviceConfiguration.Parameters has no validation + return errs +} + +// Validate_ResourceClaim validates an instance of ResourceClaim according +// to declarative validation rules in the API schema. +func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaim) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaim.TypeMeta has no validation + // field resourcev1beta2.ResourceClaim.ObjectMeta has no validation + + // field resourcev1beta2.ResourceClaim.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.Immutable(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaim) *resourcev1beta2.ResourceClaimSpec { return &oldObj.Spec }), oldObj != nil)...) + + // field resourcev1beta2.ResourceClaim.Status + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaim) *resourcev1beta2.ResourceClaimStatus { + return &oldObj.Status + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimList validates an instance of ResourceClaimList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimList) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimList.TypeMeta has no validation + // field resourcev1beta2.ResourceClaimList.ListMeta has no validation + + // field resourcev1beta2.ResourceClaimList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaim)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimList) []resourcev1beta2.ResourceClaim { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimSpec validates an instance of ResourceClaimSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimSpec) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClaim, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_DeviceClaim(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimSpec) *resourcev1beta2.DeviceClaim { return &oldObj.Devices }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimStatus) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimStatus.Allocation + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationResult, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdatePointer(ctx, op, fldPath, obj, oldObj, validate.NoModify); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) *resourcev1beta2.AllocationResult { + return oldObj.Allocation + }), oldObj != nil)...) + + // field resourcev1beta2.ResourceClaimStatus.ReservedFor + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceClaimConsumerReference, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 256); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.ResourceClaimConsumerReference, b resourcev1beta2.ResourceClaimConsumerReference) bool { + return a.UID == b.UID + })...) + return + }(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) []resourcev1beta2.ResourceClaimConsumerReference { + return oldObj.ReservedFor + }), oldObj != nil)...) + + // field resourcev1beta2.ResourceClaimStatus.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.AllocatedDeviceStatus, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.AllocatedDeviceStatus, b resourcev1beta2.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.AllocatedDeviceStatus, b resourcev1beta2.AllocatedDeviceStatus) bool { + return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID)) + }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) []resourcev1beta2.AllocatedDeviceStatus { + return oldObj.Devices + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplate validates an instance of ResourceClaimTemplate according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplate(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimTemplate) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimTemplate.TypeMeta has no validation + // field resourcev1beta2.ResourceClaimTemplate.ObjectMeta has no validation + + // field resourcev1beta2.ResourceClaimTemplate.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimTemplateSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimTemplateSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimTemplate) *resourcev1beta2.ResourceClaimTemplateSpec { + return &oldObj.Spec + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateList validates an instance of ResourceClaimTemplateList according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimTemplateList) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimTemplateList.TypeMeta has no validation + // field resourcev1beta2.ResourceClaimTemplateList.ListMeta has no validation + + // field resourcev1beta2.ResourceClaimTemplateList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceClaimTemplate, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceClaimTemplate)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimTemplateList) []resourcev1beta2.ResourceClaimTemplate { + return oldObj.Items + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceClaimTemplateSpec validates an instance of ResourceClaimTemplateSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceClaimTemplateSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimTemplateSpec) (errs field.ErrorList) { + // field resourcev1beta2.ResourceClaimTemplateSpec.ObjectMeta has no validation + + // field resourcev1beta2.ResourceClaimTemplateSpec.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceClaimSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimTemplateSpec) *resourcev1beta2.ResourceClaimSpec { + return &oldObj.Spec + }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSlice validates an instance of ResourceSlice according +// to declarative validation rules in the API schema. +func Validate_ResourceSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceSlice) (errs field.ErrorList) { + // field resourcev1beta2.ResourceSlice.TypeMeta has no validation + // field resourcev1beta2.ResourceSlice.ObjectMeta has no validation + + // field resourcev1beta2.ResourceSlice.Spec + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceSliceSpec, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_ResourceSliceSpec(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceSlice) *resourcev1beta2.ResourceSliceSpec { return &oldObj.Spec }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceList validates an instance of ResourceSliceList according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceSliceList) (errs field.ErrorList) { + // field resourcev1beta2.ResourceSliceList.TypeMeta has no validation + // field resourcev1beta2.ResourceSliceList.ListMeta has no validation + + // field resourcev1beta2.ResourceSliceList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceSlice, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_ResourceSlice)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceSliceList) []resourcev1beta2.ResourceSlice { return oldObj.Items }), oldObj != nil)...) + + return errs +} + +// Validate_ResourceSliceSpec validates an instance of ResourceSliceSpec according +// to declarative validation rules in the API schema. +func Validate_ResourceSliceSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceSliceSpec) (errs field.ErrorList) { + // field resourcev1beta2.ResourceSliceSpec.Driver has no validation + // field resourcev1beta2.ResourceSliceSpec.Pool has no validation + // field resourcev1beta2.ResourceSliceSpec.NodeName has no validation + // field resourcev1beta2.ResourceSliceSpec.NodeSelector has no validation + // field resourcev1beta2.ResourceSliceSpec.AllNodes has no validation + + // field resourcev1beta2.ResourceSliceSpec.Devices + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.Device, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Device)...) + return + }(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceSliceSpec) []resourcev1beta2.Device { return oldObj.Devices }), oldObj != nil)...) + + // field resourcev1beta2.ResourceSliceSpec.PerDeviceNodeSelection has no validation + + // field resourcev1beta2.ResourceSliceSpec.SharedCounters + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []resourcev1beta2.CounterSet, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 8); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.CounterSet, b resourcev1beta2.CounterSet) bool { return a.Name == b.Name })...) + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.CounterSet, b resourcev1beta2.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet)...) + return + }(fldPath.Child("sharedCounters"), obj.SharedCounters, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceSliceSpec) []resourcev1beta2.CounterSet { + return oldObj.SharedCounters + }), oldObj != nil)...) + + return errs +} diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index b06472a453756..ef2b4cfe82603 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -17,17 +17,23 @@ limitations under the License. package validation import ( + "context" "encoding/json" "errors" "fmt" "regexp" + "slices" "strconv" "strings" "github.com/google/uuid" + corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/operation" apiresource "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/api/validate" + "k8s.io/apimachinery/pkg/api/validate/content" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" "k8s.io/apimachinery/pkg/runtime" @@ -46,6 +52,20 @@ import ( "k8s.io/kubernetes/pkg/features" ) +// ResourceNormalizationRules handles the structural differences between v1beta1 +// (flattened fields) and v1/v1beta2 (fields under 'exactly') for validation error paths. +var ResourceNormalizationRules = []field.NormalizationRule{ + { + Regexp: regexp.MustCompile(`spec.devices\.requests\[(\d+)\]\.(deviceClassName|selectors|allocationMode|count|adminAccess|tolerations)`), + Replacement: "spec.devices.requests[$1].exactly.$2", + }, + { + // This v1beta1 'basic' to flattened rule is to support ResourceSlice + Regexp: regexp.MustCompile(`spec.devices\[(\d+)\]\.basic\.`), + Replacement: "spec.devices[$1].", + }, +} + var ( // validateResourceDriverName reuses the validation of a CSI driver because // the allowed values are exactly the same. @@ -65,11 +85,11 @@ func validatePoolName(name string, fldPath *field.Path) field.ErrorList { allErrs = append(allErrs, field.Required(fldPath, "")) } else { if len(name) > resource.PoolNameMaxLength { - allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.PoolNameMaxLength)) + allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.PoolNameMaxLength).WithOrigin("format=k8s-resource-pool-name")) } parts := strings.Split(name, "/") for _, part := range parts { - allErrs = append(allErrs, corevalidation.ValidateDNS1123Subdomain(part, fldPath)...) + allErrs = append(allErrs, corevalidation.ValidateDNS1123Subdomain(part, fldPath).WithOrigin("format=k8s-resource-pool-name")...) } } return allErrs @@ -82,7 +102,7 @@ func validateUID(uid string, fldPath *field.Path) field.ErrorList { } else if len(uid) != 36 || uid != strings.ToLower(uid) { allErrs = append(allErrs, field.Invalid(fldPath, uid, "uid must be in RFC 4122 normalized form, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with lowercase hexadecimal characters")) } - return allErrs + return allErrs.WithOrigin("format=k8s-uuid") } // ValidateResourceClaim validates a ResourceClaim. @@ -94,12 +114,12 @@ func ValidateResourceClaim(resourceClaim *resource.ResourceClaim) field.ErrorLis // ValidateResourceClaimUpdate tests if an update to ResourceClaim is valid. func ValidateResourceClaimUpdate(resourceClaim, oldClaim *resource.ResourceClaim) field.ErrorList { - allErrs := corevalidation.ValidateObjectMetaUpdate(&resourceClaim.ObjectMeta, &oldClaim.ObjectMeta, field.NewPath("metadata")) - allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(resourceClaim.Spec, oldClaim.Spec, field.NewPath("spec"))...) - // Because the spec is immutable, all CEL expressions in it must have been stored. - // If the user tries an update, this is not true and checking is less strict, but - // as there are errors, it doesn't matter. - allErrs = append(allErrs, validateResourceClaimSpec(&resourceClaim.Spec, field.NewPath("spec"), true)...) + allErrs := corevalidation.ValidateObjectMeta(&resourceClaim.ObjectMeta, true, corevalidation.ValidateResourceClaimName, field.NewPath("metadata")) + allErrs = append(allErrs, corevalidation.ValidateObjectMetaUpdate(&resourceClaim.ObjectMeta, &oldClaim.ObjectMeta, field.NewPath("metadata"))...) + // The spec is immutable. On update, we only check for immutability. + // Re-validating other fields is skipped because the user cannot change them; + // the only actionable error is for the immutability violation. + allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(resourceClaim.Spec, oldClaim.Spec, field.NewPath("spec")).WithOrigin("immutable").MarkCoveredByDeclarative()...) return allErrs } @@ -124,18 +144,18 @@ func validateDeviceClaim(deviceClaim *resource.DeviceClaim, fldPath *field.Path, func(request resource.DeviceRequest, fldPath *field.Path) field.ErrorList { return validateDeviceRequest(request, fldPath, stored) }, - func(request resource.DeviceRequest) (string, string) { - return request.Name, "name" + func(request resource.DeviceRequest) string { + return request.Name }, - fldPath.Child("requests"))...) + fldPath.Child("requests"), sizeCovered, uniquenessCovered)...) allErrs = append(allErrs, validateSlice(deviceClaim.Constraints, resource.DeviceConstraintsMaxSize, func(constraint resource.DeviceConstraint, fldPath *field.Path) field.ErrorList { return validateDeviceConstraint(constraint, fldPath, requestNames) - }, fldPath.Child("constraints"))...) + }, fldPath.Child("constraints"), sizeCovered)...) allErrs = append(allErrs, validateSlice(deviceClaim.Config, resource.DeviceConfigMaxSize, func(config resource.DeviceClaimConfiguration, fldPath *field.Path) field.ErrorList { return validateDeviceClaimConfiguration(config, fldPath, requestNames, stored) - }, fldPath.Child("config"))...) + }, fldPath.Child("config"), sizeCovered)...) return allErrs } @@ -191,38 +211,41 @@ func gatherAllocatedDevices(allocationResult *resource.DeviceAllocationResult) s func validateDeviceRequest(request resource.DeviceRequest, fldPath *field.Path, stored bool) field.ErrorList { allErrs := validateRequestName(request.Name, fldPath.Child("name")) - numDeviceRequestType := 0 - if len(request.FirstAvailable) > 0 { + + hasFirstAvailable := len(request.FirstAvailable) > 0 + hasExactly := request.Exactly != nil + + if hasFirstAvailable { numDeviceRequestType++ - allErrs = append(allErrs, validateSet(request.FirstAvailable, resource.FirstAvailableDeviceRequestMaxSize, - func(subRequest resource.DeviceSubRequest, fldPath *field.Path) field.ErrorList { - return validateDeviceSubRequest(subRequest, fldPath, stored) - }, - func(subRequest resource.DeviceSubRequest) (string, string) { - return subRequest.Name, "name" - }, - fldPath.Child("firstAvailable"))...) } - - if request.Exactly != nil { + if hasExactly { numDeviceRequestType++ - allErrs = append(allErrs, validateExactDeviceRequest(*request.Exactly, fldPath.Child("exactly"), stored)...) } - switch numDeviceRequestType { - case 0: + switch { + case numDeviceRequestType == 0: allErrs = append(allErrs, field.Required(fldPath, "exactly one of `exactly` or `firstAvailable` is required")) - case 1: - default: + case numDeviceRequestType > 1: allErrs = append(allErrs, field.Invalid(fldPath, nil, "exactly one of `exactly` or `firstAvailable` is required, but multiple fields are set")) + case hasFirstAvailable: + allErrs = append(allErrs, validateSet(request.FirstAvailable, resource.FirstAvailableDeviceRequestMaxSize, + func(subRequest resource.DeviceSubRequest, fldPath *field.Path) field.ErrorList { + return validateDeviceSubRequest(subRequest, fldPath, stored) + }, + func(subRequest resource.DeviceSubRequest) string { + return subRequest.Name + }, + fldPath.Child("firstAvailable"), sizeCovered, uniquenessCovered)...) + case hasExactly: + allErrs = append(allErrs, validateExactDeviceRequest(*request.Exactly, fldPath.Child("exactly"), stored)...) } return allErrs } func validateDeviceSubRequest(subRequest resource.DeviceSubRequest, fldPath *field.Path, stored bool) field.ErrorList { allErrs := validateRequestName(subRequest.Name, fldPath.Child("name")) - allErrs = append(allErrs, validateDeviceClass(subRequest.DeviceClassName, fldPath.Child("deviceClassName"))...) + allErrs = append(allErrs, validateDeviceClass(subRequest.DeviceClassName, fldPath.Child("deviceClassName")).MarkCoveredByDeclarative()...) allErrs = append(allErrs, validateSelectorSlice(subRequest.Selectors, fldPath.Child("selectors"), stored)...) allErrs = append(allErrs, validateDeviceAllocationMode(subRequest.AllocationMode, subRequest.Count, fldPath.Child("allocationMode"), fldPath.Child("count"))...) for i, toleration := range subRequest.Tolerations { @@ -254,7 +277,10 @@ func validateDeviceAllocationMode(deviceAllocationMode resource.DeviceAllocation allErrs = append(allErrs, field.Invalid(countFldPath, count, "must be greater than zero")) } default: - allErrs = append(allErrs, field.NotSupported(allocModeFldPath, deviceAllocationMode, []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount})) + // NOTE: Declarative validation does not (yet) enforce the real requiredness of this field + // because v1beta1 uses a bespoke form of union which makes this field truly optional + // in some cases and truly required in others. DO NOT REMOVE THIS CODE UNLESS THAT IS RESOLVED. + allErrs = append(allErrs, field.NotSupported(allocModeFldPath, deviceAllocationMode, []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount}).MarkCoveredByDeclarative()) } return allErrs } @@ -274,7 +300,7 @@ func validateSelectorSlice(selectors []resource.DeviceSelector, fldPath *field.P func(selector resource.DeviceSelector, fldPath *field.Path) field.ErrorList { return validateSelector(selector, fldPath, stored) }, - fldPath) + fldPath, sizeCovered) } func validateSelector(selector resource.DeviceSelector, fldPath *field.Path, stored bool) field.ErrorList { @@ -330,9 +356,9 @@ func validateDeviceConstraint(constraint resource.DeviceConstraint, fldPath *fie func(name string, fldPath *field.Path) field.ErrorList { return validateRequestNameRef(name, fldPath, requestNames) }, - stringKey, fldPath.Child("requests"))...) + stringKey, fldPath.Child("requests"), sizeCovered, uniquenessCovered)...) if constraint.MatchAttribute != nil { - allErrs = append(allErrs, validateFullyQualifiedName(*constraint.MatchAttribute, fldPath.Child("matchAttribute"))...) + allErrs = append(allErrs, validateFullyQualifiedName(*constraint.MatchAttribute, fldPath.Child("matchAttribute")).MarkCoveredByDeclarative()...) } else if constraint.DistinctAttribute != nil { allErrs = append(allErrs, validateFullyQualifiedName(*constraint.DistinctAttribute, fldPath.Child("distinctAttribute"))...) } else if utilfeature.DefaultFeatureGate.Enabled(features.DRAConsumableCapacity) { @@ -348,7 +374,7 @@ func validateDeviceClaimConfiguration(config resource.DeviceClaimConfiguration, allErrs = append(allErrs, validateSet(config.Requests, resource.DeviceRequestsMaxSize, func(name string, fldPath *field.Path) field.ErrorList { return validateRequestNameRef(name, fldPath, requestNames) - }, stringKey, fldPath.Child("requests"))...) + }, stringKey, fldPath.Child("requests"), sizeCovered, uniquenessCovered)...) allErrs = append(allErrs, validateDeviceConfiguration(config.DeviceConfiguration, fldPath, stored)...) return allErrs } @@ -383,7 +409,7 @@ func validateDeviceConfiguration(config resource.DeviceConfiguration, fldPath *f func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldPath *field.Path, stored bool) field.ErrorList { var allErrs field.ErrorList - allErrs = append(allErrs, validateDriverName(config.Driver, fldPath.Child("driver"))...) + allErrs = append(allErrs, validateDriverName(config.Driver, fldPath.Child("driver"), corevalidation.RequiredCovered, corevalidation.FormatCovered)...) allErrs = append(allErrs, validateRawExtension(config.Parameters, fldPath.Child("parameters"), stored, resource.OpaqueParametersMaxLength)...) return allErrs } @@ -392,8 +418,8 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim var allErrs field.ErrorList allErrs = append(allErrs, validateSet(status.ReservedFor, resource.ResourceClaimReservedForMaxSize, validateResourceClaimUserReference, - func(consumer resource.ResourceClaimConsumerReference) (types.UID, string) { return consumer.UID, "uid" }, - fldPath.Child("reservedFor"))...) + func(consumer resource.ResourceClaimConsumerReference) types.UID { return consumer.UID }, + fldPath.Child("reservedFor"), sizeCovered, uniquenessCovered)...) var allocatedDevices sets.Set[structured.SharedDeviceID] if status.Allocation != nil { @@ -403,11 +429,11 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim func(device resource.AllocatedDeviceStatus, fldPath *field.Path) field.ErrorList { return validateDeviceStatus(device, fldPath, allocatedDevices) }, - func(device resource.AllocatedDeviceStatus) (structured.SharedDeviceID, string) { + func(device resource.AllocatedDeviceStatus) structured.SharedDeviceID { deviceID := structured.MakeDeviceID(device.Driver, device.Pool, device.Device) - return structured.MakeSharedDeviceID(deviceID, (*types.UID)(device.ShareID)), "deviceID" + return structured.MakeSharedDeviceID(deviceID, (*types.UID)(device.ShareID)) }, - fldPath.Child("devices"))...) + fldPath.Child("devices"), uniquenessCovered)...) // Now check for invariants that must be valid for a ResourceClaim. if len(status.ReservedFor) > 0 { @@ -432,7 +458,7 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim // in this particular case, must not be validated again because // validation for new results is tighter than it was before. if oldStatus.Allocation != nil && status.Allocation != nil { - allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(status.Allocation, oldStatus.Allocation, fldPath.Child("allocation"))...) + allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(status.Allocation, oldStatus.Allocation, fldPath.Child("allocation")).WithOrigin("update").MarkCoveredByDeclarative()...) } else if status.Allocation != nil { allErrs = append(allErrs, validateAllocationResult(status.Allocation, fldPath.Child("allocation"), requestNames, false)...) } @@ -471,11 +497,11 @@ func validateDeviceAllocationResult(allocation resource.DeviceAllocationResult, allErrs = append(allErrs, validateSlice(allocation.Results, resource.AllocationResultsMaxSize, func(result resource.DeviceRequestAllocationResult, fldPath *field.Path) field.ErrorList { return validateDeviceRequestAllocationResult(result, fldPath, requestNames) - }, fldPath.Child("results"))...) + }, fldPath.Child("results"), sizeCovered)...) allErrs = append(allErrs, validateSlice(allocation.Config, 2*resource.DeviceConfigMaxSize, /* class + claim */ func(config resource.DeviceAllocationConfiguration, fldPath *field.Path) field.ErrorList { return validateDeviceAllocationConfiguration(config, fldPath, requestNames, stored) - }, fldPath.Child("config"))...) + }, fldPath.Child("config"), sizeCovered)...) return allErrs } @@ -483,12 +509,12 @@ func validateDeviceAllocationResult(allocation resource.DeviceAllocationResult, func validateDeviceRequestAllocationResult(result resource.DeviceRequestAllocationResult, fldPath *field.Path, requestNames requestNames) field.ErrorList { var allErrs field.ErrorList allErrs = append(allErrs, validateRequestNameRef(result.Request, fldPath.Child("request"), requestNames)...) - allErrs = append(allErrs, validateDriverName(result.Driver, fldPath.Child("driver"))...) - allErrs = append(allErrs, validatePoolName(result.Pool, fldPath.Child("pool"))...) + allErrs = append(allErrs, validateDriverName(result.Driver, fldPath.Child("driver"), corevalidation.RequiredCovered, corevalidation.FormatCovered)...) + allErrs = append(allErrs, validatePoolName(result.Pool, fldPath.Child("pool")).MarkCoveredByDeclarative()...) allErrs = append(allErrs, validateDeviceName(result.Device, fldPath.Child("device"))...) allErrs = append(allErrs, validateDeviceBindingParameters(result.BindingConditions, result.BindingFailureConditions, fldPath)...) if result.ShareID != nil { - allErrs = append(allErrs, validateUID(string(*result.ShareID), fldPath.Child("shareID"))...) + allErrs = append(allErrs, validateUID(string(*result.ShareID), fldPath.Child("shareID")).MarkCoveredByDeclarative()...) } return allErrs } @@ -499,7 +525,7 @@ func validateDeviceAllocationConfiguration(config resource.DeviceAllocationConfi allErrs = append(allErrs, validateSet(config.Requests, resource.DeviceRequestsMaxSize, func(name string, fldPath *field.Path) field.ErrorList { return validateRequestNameRef(name, fldPath, requestNames) - }, stringKey, fldPath.Child("requests"))...) + }, stringKey, fldPath.Child("requests"), sizeCovered, uniquenessCovered)...) allErrs = append(allErrs, validateDeviceConfiguration(config.DeviceConfiguration, fldPath, stored)...) return allErrs } @@ -508,24 +534,34 @@ func validateAllocationConfigSource(source resource.AllocationConfigSource, fldP var allErrs field.ErrorList switch source { case "": - allErrs = append(allErrs, field.Required(fldPath, "")) + allErrs = append(allErrs, field.Required(fldPath, "").MarkCoveredByDeclarative()) case resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass: default: - allErrs = append(allErrs, field.NotSupported(fldPath, source, []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass})) + allErrs = append(allErrs, field.NotSupported(fldPath, source, []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}).MarkCoveredByDeclarative()) } return allErrs } // ValidateDeviceClass validates a DeviceClass. func ValidateDeviceClass(class *resource.DeviceClass) field.ErrorList { - allErrs := corevalidation.ValidateObjectMeta(&class.ObjectMeta, false, corevalidation.ValidateClassName, field.NewPath("metadata")) + validateClassName := func(fldPath *field.Path, name string) field.ErrorList { + // validate.LongName doesn't respect operation type currently (CREATE or UPDATE) + // so it is ok to use operation.Operation{} here + return validate.LongName(context.Background(), operation.Operation{}, fldPath, &name, nil).MarkCoveredByDeclarative() + } + allErrs := corevalidation.ValidateObjectMetaWithOpts(&class.ObjectMeta, false, validateClassName, field.NewPath("metadata")) allErrs = append(allErrs, validateDeviceClassSpec(&class.Spec, nil, field.NewPath("spec"))...) return allErrs } // ValidateDeviceClassUpdate tests if an update to DeviceClass is valid. func ValidateDeviceClassUpdate(class, oldClass *resource.DeviceClass) field.ErrorList { - allErrs := corevalidation.ValidateObjectMetaUpdate(&class.ObjectMeta, &oldClass.ObjectMeta, field.NewPath("metadata")) + validateClassName := func(fldPath *field.Path, name string) field.ErrorList { + return validate.LongName(context.Background(), operation.Operation{}, fldPath, &name, nil).MarkCoveredByDeclarative() + } + // TODO(lalitc375): Remove this if decided in https://github.com/kubernetes/kubernetes/issues/134444. + allErrs := corevalidation.ValidateObjectMetaWithOpts(&class.ObjectMeta, false, validateClassName, field.NewPath("metadata")) + allErrs = append(allErrs, corevalidation.ValidateObjectMetaUpdate(&class.ObjectMeta, &oldClass.ObjectMeta, field.NewPath("metadata"))...) allErrs = append(allErrs, validateDeviceClassSpec(&class.Spec, &oldClass.Spec, field.NewPath("spec"))...) return allErrs } @@ -543,8 +579,9 @@ func validateDeviceClassSpec(spec, oldSpec *resource.DeviceClassSpec, fldPath *f func(selector resource.DeviceSelector, fldPath *field.Path) field.ErrorList { return validateSelector(selector, fldPath, stored) }, - fldPath.Child("selectors"))...) + fldPath.Child("selectors"), sizeCovered)...) // Same logic as above for configs. + stored = false if oldSpec != nil { stored = apiequality.Semantic.DeepEqual(spec.Config, oldSpec.Config) } @@ -552,10 +589,10 @@ func validateDeviceClassSpec(spec, oldSpec *resource.DeviceClassSpec, fldPath *f func(config resource.DeviceClassConfiguration, fldPath *field.Path) field.ErrorList { return validateDeviceClassConfiguration(config, fldPath, stored) }, - fldPath.Child("config"))...) + fldPath.Child("config"), sizeCovered)...) if spec.ExtendedResourceName != nil && !v1helper.IsExtendedResourceName(corev1.ResourceName(*spec.ExtendedResourceName)) { allErrs = append(allErrs, field.Invalid(fldPath.Child("extendedResourceName"), *spec.ExtendedResourceName, - "must be a valid extended resource name")) + "must be a valid extended resource name").MarkCoveredByDeclarative().WithOrigin("format=k8s-extended-resource-name")) } return allErrs } @@ -580,11 +617,10 @@ func validateResourceClaimTemplateSpec(spec *resource.ResourceClaimTemplateSpec, // ValidateResourceClaimTemplateUpdate tests if an update to template is valid. func ValidateResourceClaimTemplateUpdate(template, oldTemplate *resource.ResourceClaimTemplate) field.ErrorList { allErrs := corevalidation.ValidateObjectMetaUpdate(&template.ObjectMeta, &oldTemplate.ObjectMeta, field.NewPath("metadata")) + // The spec is immutable. On update, we only check for immutability. + // Re-validating other fields is skipped because the user cannot change them; + // the only actionable error is for the immutability violation. allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(template.Spec, oldTemplate.Spec, field.NewPath("spec"))...) - // Because the spec is immutable, all CEL expressions in it must have been stored. - // If the user tries an update, this is not true and checking is less strict, but - // as there are errors, it doesn't matter. - allErrs = append(allErrs, validateResourceClaimTemplateSpec(&template.Spec, field.NewPath("spec"), true)...) return allErrs } @@ -667,73 +703,89 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat "exactly one of `nodeName`, `nodeSelector`, `allNodes`, `perDeviceNodeSelection` is required, but multiple fields are set")) } - sharedCounterToCounterNames := gatherSharedCounterCounterNames(spec.SharedCounters) - allErrs = append(allErrs, validateSet(spec.Devices, resource.ResourceSliceMaxDevices, + if spec.SharedCounters != nil && spec.Devices != nil { + allErrs = append(allErrs, field.Invalid(fldPath, "", "only one of `sharedCounters` or `devices` is allowed")) + } + + maxDevices := resource.ResourceSliceMaxDevices + if haveDeviceTaints(spec) || haveConsumesCounters(spec) { + maxDevices = resource.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters + } + allErrs = append(allErrs, validateSet(spec.Devices, maxDevices, func(device resource.Device, fldPath *field.Path) field.ErrorList { - return validateDevice(device, fldPath, sharedCounterToCounterNames, spec.PerDeviceNodeSelection) + oldDevice := lookupDevice(oldSpec, device.Name) + return validateDevice(device, oldDevice, fldPath, spec.PerDeviceNodeSelection) }, - func(device resource.Device) (string, string) { - return device.Name, "name" + func(device resource.Device) string { + return device.Name }, fldPath.Child("devices"))...) - // Size limit for total number of counters in devices enforced here. - numDeviceCounters := 0 + allErrs = append(allErrs, validateSet(spec.SharedCounters, resource.ResourceSliceMaxCounterSets, + validateCounterSet, + func(counterSet resource.CounterSet) string { + return counterSet.Name + }, fldPath.Child("sharedCounters"), sizeCovered, uniquenessCovered)...) + + return allErrs +} + +func haveDeviceTaints(spec *resource.ResourceSliceSpec) bool { + if spec == nil { + return false + } + for _, device := range spec.Devices { - for _, c := range device.ConsumesCounters { - numDeviceCounters += len(c.Counters) + if len(device.Taints) > 0 { + return true } } - if numDeviceCounters > resource.ResourceSliceMaxDeviceCountersPerSlice { - allErrs = append(allErrs, field.Invalid(fldPath.Child("devices"), numDeviceCounters, fmt.Sprintf("the total number of counters in devices must not exceed %d", resource.ResourceSliceMaxDeviceCountersPerSlice))) - } + return false +} - // Size limit for all shared counters enforced here. - numCounters := 0 - for _, set := range spec.SharedCounters { - numCounters += len(set.Counters) +func haveConsumesCounters(spec *resource.ResourceSliceSpec) bool { + if spec == nil { + return false } - if numCounters > resource.ResourceSliceMaxSharedCounters { - allErrs = append(allErrs, field.Invalid(fldPath.Child("sharedCounters"), numCounters, fmt.Sprintf("the total number of shared counters must not exceed %d", resource.ResourceSliceMaxSharedCounters))) + + for _, device := range spec.Devices { + if len(device.ConsumesCounters) > 0 { + return true + } } - allErrs = append(allErrs, validateSet(spec.SharedCounters, -1, - validateCounterSet, - func(counterSet resource.CounterSet) (string, string) { - return counterSet.Name, "name" - }, fldPath.Child("sharedCounters"))...) + return false +} - return allErrs +func lookupDevice(spec *resource.ResourceSliceSpec, deviceName string) *resource.Device { + if spec == nil { + return nil + } + for i := range spec.Devices { + device := &spec.Devices[i] + if device.Name == deviceName { + return device + } + } + return nil } func validateCounterSet(counterSet resource.CounterSet, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList if counterSet.Name == "" { - allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) + allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative()) } else { - allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name"))...) + allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name"))...).MarkCoveredByDeclarative() } if len(counterSet.Counters) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("counters"), "")) } else { // The size limit is enforced for across all sets by the caller. - allErrs = append(allErrs, validateMap(counterSet.Counters, -1, validation.DNS1123LabelMaxLength, + allErrs = append(allErrs, validateMap(counterSet.Counters, resource.ResourceSliceMaxCountersPerCounterSet, validation.DNS1123LabelMaxLength, validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...) } return allErrs } -func gatherSharedCounterCounterNames(sharedCounters []resource.CounterSet) map[string]sets.Set[string] { - sharedCounterToCounterMap := make(map[string]sets.Set[string]) - for _, counterSet := range sharedCounters { - counterNames := sets.New[string]() - for counterName := range counterSet.Counters { - counterNames.Insert(counterName) - } - sharedCounterToCounterMap[counterSet.Name] = counterNames - } - return sharedCounterToCounterMap -} - func validateResourcePool(pool resource.ResourcePool, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList allErrs = append(allErrs, validatePoolName(pool.Name, fldPath.Child("name"))...) @@ -746,7 +798,7 @@ func validateResourcePool(pool resource.ResourcePool, fldPath *field.Path) field return allErrs } -func validateDevice(device resource.Device, fldPath *field.Path, sharedCounterToCounterNames map[string]sets.Set[string], perDeviceNodeSelection *bool) field.ErrorList { +func validateDevice(device resource.Device, oldDevice *resource.Device, fldPath *field.Path, perDeviceNodeSelection *bool) field.ErrorList { var allErrs field.ErrorList allowMultipleAllocations := device.AllowMultipleAllocations != nil && *device.AllowMultipleAllocations allErrs = append(allErrs, validateDeviceName(device.Name, fldPath.Child("name"))...) @@ -763,35 +815,21 @@ func validateDevice(device resource.Device, fldPath *field.Path, sharedCounterTo } else { allErrs = append(allErrs, validateMap(device.Capacity, -1, attributeAndCapacityMaxKeyLength, validateQualifiedName, validateSingleAllocatableDeviceCapacity, fldPath.Child("capacity"))...) } - allErrs = append(allErrs, validateSlice(device.Taints, resource.DeviceTaintsMaxLength, validateDeviceTaint, fldPath.Child("taints"))...) - - allErrs = append(allErrs, validateSet(device.ConsumesCounters, -1, - validateDeviceCounterConsumption, - func(deviceCapacityConsumption resource.DeviceCounterConsumption) (string, string) { - return deviceCapacityConsumption.CounterSet, "counterSet" - }, fldPath.Child("consumesCounters"))...) - - var countersLength int - for _, set := range device.ConsumesCounters { - countersLength += len(set.Counters) - } - if countersLength > resource.ResourceSliceMaxCountersPerDevice { - allErrs = append(allErrs, field.Invalid(fldPath, countersLength, fmt.Sprintf("the total number of counters must not exceed %d", resource.ResourceSliceMaxCountersPerDevice))) + // If the entire set is the same as before then validation can be skipped. + // We could also do the DeepEqual on the entire spec, but here it is a bit cheaper. + if oldDevice == nil || !apiequality.Semantic.DeepEqual(oldDevice.Taints, device.Taints) { + allErrs = append(allErrs, validateSlice(device.Taints, resource.DeviceTaintsMaxLength, + func(taint resource.DeviceTaint, fldPath *field.Path) field.ErrorList { + return validateDeviceTaint(taint, nil, fldPath) + }, + fldPath.Child("taints"))...) } - for i, deviceCounterConsumption := range device.ConsumesCounters { - if capacityNames, exists := sharedCounterToCounterNames[deviceCounterConsumption.CounterSet]; exists { - for capacityName := range deviceCounterConsumption.Counters { - if !capacityNames.Has(string(capacityName)) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("consumesCounters").Index(i).Child("counters"), - capacityName, "must reference a counter defined in the ResourceSlice sharedCounters")) - } - } - } else { - allErrs = append(allErrs, field.Invalid(fldPath.Child("consumesCounters").Index(i).Child("counterSet"), - deviceCounterConsumption.CounterSet, "must reference a counterSet defined in the ResourceSlice sharedCounters")) - } - } + allErrs = append(allErrs, validateSet(device.ConsumesCounters, resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice, + validateDeviceCounterConsumption, + func(deviceCapacityConsumption resource.DeviceCounterConsumption) string { + return deviceCapacityConsumption.CounterSet + }, fldPath.Child("consumesCounters"), sizeCovered, uniquenessCovered)...) if perDeviceNodeSelection != nil && *perDeviceNodeSelection { setFields := make([]string, 0, 3) @@ -834,14 +872,15 @@ func validateDeviceCounterConsumption(deviceCounterConsumption resource.DeviceCo var allErrs field.ErrorList if len(deviceCounterConsumption.CounterSet) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("counterSet"), "")) + allErrs = append(allErrs, field.Required(fldPath.Child("counterSet"), "").MarkCoveredByDeclarative()) + } else { + allErrs = append(allErrs, validateCounterName(deviceCounterConsumption.CounterSet, fldPath.Child("counterSet"))...).MarkCoveredByDeclarative() } if len(deviceCounterConsumption.Counters) == 0 { allErrs = append(allErrs, field.Required(fldPath.Child("counters"), "")) } else { - // The size limit is enforced for the entire device. - allErrs = append(allErrs, validateMap(deviceCounterConsumption.Counters, -1, validation.DNS1123LabelMaxLength, - validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...) + allErrs = append(allErrs, validateMap(deviceCounterConsumption.Counters, resource.ResourceSliceMaxCountersPerDeviceCounterConsumption, + validation.DNS1123LabelMaxLength, validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...) } return allErrs } @@ -886,11 +925,11 @@ func validateDeviceAttribute(attribute resource.DeviceAttribute, fldPath *field. switch numFields { case 0: - allErrs = append(allErrs, field.Required(fldPath, "exactly one value must be specified")) + allErrs = append(allErrs, field.Invalid(fldPath, "", "exactly one value must be specified").MarkCoveredByDeclarative()) case 1: // Okay. default: - allErrs = append(allErrs, field.Invalid(fldPath, attribute, "exactly one value must be specified")) + allErrs = append(allErrs, field.Invalid(fldPath, attribute, "exactly one value must be specified").MarkCoveredByDeclarative()) } return allErrs } @@ -1052,38 +1091,46 @@ func validateDeviceCounter(counter resource.Counter, fldPath *field.Path) field. func validateQualifiedName(name resource.QualifiedName, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList - if name == "" { - allErrs = append(allErrs, field.Required(fldPath, "name required")) - return allErrs - } - parts := strings.Split(string(name), "/") switch len(parts) { case 1: allErrs = append(allErrs, validateCIdentifier(parts[0], fldPath)...) case 2: if len(parts[0]) == 0 { - allErrs = append(allErrs, field.Required(fldPath, "the domain must not be empty")) + allErrs = append(allErrs, field.Invalid(fldPath, "", "the domain must not be empty")) } else { allErrs = append(allErrs, validateDriverName(parts[0], fldPath)...) } if len(parts[1]) == 0 { - allErrs = append(allErrs, field.Required(fldPath, "the name must not be empty")) + allErrs = append(allErrs, field.Invalid(fldPath, "", "the name must not be empty")) } else { allErrs = append(allErrs, validateCIdentifier(parts[1], fldPath)...) } + // TODO: This validation is incomplete. It should reject qualified names + // that contain more than one slash. Currently, names like "a/b/c" are not + // handled and are implicitly accepted. + // + // This needs to be fixed in two places: + // 1. Here in this function. + // 2. In the corresponding declarative validation utility `resourcesQualifiedName` + // in `staging/src/k8s.io/apimachinery/pkg/api/validate/strfmt.go`. + // + // The fix should be introduced carefully, possibly using ratcheting to avoid + // breaking existing, non-compliant objects. } + return allErrs } func validateFullyQualifiedName(name resource.FullyQualifiedName, fldPath *field.Path) field.ErrorList { - allErrs := validateQualifiedName(resource.QualifiedName(name), fldPath) - // validateQualifiedName checks that the name isn't empty and both parts are valid. + var allErrs field.ErrorList + allErrs = append(allErrs, validateQualifiedName(resource.QualifiedName(name), fldPath)...) + // validateQualifiedName checks that both parts are valid. // What we need to enforce here is that there really is a domain. - if name != "" && !strings.Contains(string(name), "/") { - allErrs = append(allErrs, field.Invalid(fldPath, name, "must include a domain")) + if !strings.Contains(string(name), "/") { + allErrs = append(allErrs, field.Invalid(fldPath, name, "a fully qualified name must be a domain and a name separated by a slash")) } - return allErrs + return allErrs.WithOrigin("format=k8s-resource-fully-qualified-name") } func validateCIdentifier(id string, fldPath *field.Path) field.ErrorList { @@ -1091,44 +1138,84 @@ func validateCIdentifier(id string, fldPath *field.Path) field.ErrorList { if len(id) > resource.DeviceMaxIDLength { allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.DeviceMaxIDLength)) } - for _, msg := range validation.IsCIdentifier(id) { + for _, msg := range content.IsCIdentifier(id) { allErrs = append(allErrs, field.Invalid(fldPath, id, msg)) } return allErrs } -// validateSlice ensures that a slice does not exceed a certain maximum size -// and that all entries are valid. -// A negative maxSize disables the length check. -func validateSlice[T any](slice []T, maxSize int, validateItem func(T, *field.Path) field.ErrorList, fldPath *field.Path) field.ErrorList { +// validationOption is an option for validation. +type validationOption int + +const ( + // The validation of each item is covered by declarative validation. + itemsCovered validationOption = iota + // The list size check is covered by declarative validation. + sizeCovered + // The uniqueness check is covered by declarative validation. + uniquenessCovered +) + +// validateItems validates each item in a slice. +func validateItems[T any](slice []T, validateItem func(T, *field.Path) field.ErrorList, fldPath *field.Path, opts ...validationOption) field.ErrorList { var allErrs field.ErrorList for i, item := range slice { idxPath := fldPath.Index(i) - allErrs = append(allErrs, validateItem(item, idxPath)...) + errs := validateItem(item, idxPath) + if slices.Contains(opts, itemsCovered) { + errs = errs.MarkCoveredByDeclarative() + } + allErrs = append(allErrs, errs...) } + return allErrs +} + +// validateSlice ensures that a slice does not exceed a certain maximum size +// and that all entries are valid. +// A negative maxSize disables the length check. +func validateSlice[T any](slice []T, maxSize int, validateItem func(T, *field.Path) field.ErrorList, fldPath *field.Path, opts ...validationOption) field.ErrorList { if maxSize >= 0 && len(slice) > maxSize { // Dumping the entire field into the error message is likely to be too long, // in particular when it is already beyond the maximum size. Instead this // just shows the number of entries. - allErrs = append(allErrs, field.TooMany(fldPath, len(slice), maxSize)) + err := field.TooMany(fldPath, len(slice), maxSize).WithOrigin("maxItems") + if slices.Contains(opts, sizeCovered) { + err = err.MarkCoveredByDeclarative() + } + // maxSize check short-circuits for DOS protection + return field.ErrorList{err} } - return allErrs + return validateItems(slice, validateItem, fldPath, opts...) } // validateSet ensures that a slice contains no duplicates, does not // exceed a certain maximum size and that all entries are valid. -func validateSet[T any, K comparable](slice []T, maxSize int, validateItem func(item T, fldPath *field.Path) field.ErrorList, itemKey func(T) (K, string), fldPath *field.Path) field.ErrorList { - allErrs := validateSlice(slice, maxSize, validateItem, fldPath) +func validateSet[T any, K comparable](slice []T, maxSize int, validateItem func(item T, fldPath *field.Path) field.ErrorList, itemKey func(T) K, fldPath *field.Path, opts ...validationOption) field.ErrorList { + if maxSize >= 0 && len(slice) > maxSize { + // Dumping the entire field into the error message is likely to be too long, + // in particular when it is already beyond the maximum size. Instead this + // just shows the number of entries. + err := field.TooMany(fldPath, len(slice), maxSize).WithOrigin("maxItems") + if slices.Contains(opts, sizeCovered) { + err = err.MarkCoveredByDeclarative() + } + // maxSize check short-circuits for DOS protection + return field.ErrorList{err} + } + + allErrs := validateItems(slice, validateItem, fldPath, opts...) + allItems := sets.New[K]() for i, item := range slice { idxPath := fldPath.Index(i) - key, fieldName := itemKey(item) + key := itemKey(item) childPath := idxPath - if fieldName != "" { - childPath = childPath.Child(fieldName) - } if allItems.Has(key) { - allErrs = append(allErrs, field.Duplicate(childPath, key)) + err := field.Duplicate(childPath, key) + if slices.Contains(opts, uniquenessCovered) { + err = err.MarkCoveredByDeclarative() + } + allErrs = append(allErrs, err) } else { allItems.Insert(key) } @@ -1137,13 +1224,13 @@ func validateSet[T any, K comparable](slice []T, maxSize int, validateItem func( } // stringKey uses the item itself as a key for validateSet. -func stringKey(item string) (string, string) { - return item, "" +func stringKey(item string) string { + return item } // quantityKey uses the item itself as a key for validateSet. -func quantityKey(item apiresource.Quantity) (string, string) { - return strconv.FormatInt(item.Value(), 10), "" +func quantityKey(item apiresource.Quantity) string { + return strconv.FormatInt(item.Value(), 10) } // validateMap validates keys, items and the maximum length of a map. @@ -1157,6 +1244,8 @@ func validateMap[K ~string, T any](m map[K]T, maxSize, truncateKeyLen int, valid var allErrs field.ErrorList if maxSize >= 0 && len(m) > maxSize { allErrs = append(allErrs, field.TooMany(fldPath, len(m), maxSize)) + // maxSize check short-circuits for DOS protection + return allErrs } for key, item := range m { keyPath := fldPath.Key(truncateIfTooLong(string(key), truncateKeyLen)) @@ -1186,7 +1275,7 @@ func validateDeviceStatus(device resource.AllocatedDeviceStatus, fldPath *field. allErrs = append(allErrs, validatePoolName(device.Pool, fldPath.Child("pool"))...) allErrs = append(allErrs, validateDeviceName(device.Device, fldPath.Child("device"))...) if device.ShareID != nil { - allErrs = append(allErrs, validateUID(*device.ShareID, fldPath.Child("shareID"))...) + allErrs = append(allErrs, validateUID(*device.ShareID, fldPath.Child("shareID")).MarkCoveredByDeclarative()...) } deviceID := structured.MakeDeviceID(device.Driver, device.Pool, device.Device) sharedDeviceID := structured.MakeSharedDeviceID(deviceID, (*types.UID)(device.ShareID)) @@ -1233,17 +1322,17 @@ func validateNetworkDeviceData(networkDeviceData *resource.NetworkDeviceData, fl } if len(networkDeviceData.InterfaceName) > resource.NetworkDeviceDataInterfaceNameMaxLength { - allErrs = append(allErrs, field.TooLong(fldPath.Child("interfaceName"), "" /* unused */, resource.NetworkDeviceDataInterfaceNameMaxLength)) + allErrs = append(allErrs, field.TooLong(fldPath.Child("interfaceName"), "" /* unused */, resource.NetworkDeviceDataInterfaceNameMaxLength).WithOrigin("maxLength").MarkCoveredByDeclarative()) } if len(networkDeviceData.HardwareAddress) > resource.NetworkDeviceDataHardwareAddressMaxLength { - allErrs = append(allErrs, field.TooLong(fldPath.Child("hardwareAddress"), "" /* unused */, resource.NetworkDeviceDataHardwareAddressMaxLength)) + allErrs = append(allErrs, field.TooLong(fldPath.Child("hardwareAddress"), "" /* unused */, resource.NetworkDeviceDataHardwareAddressMaxLength).WithOrigin("maxLength").MarkCoveredByDeclarative()) } allErrs = append(allErrs, validateSet(networkDeviceData.IPs, resource.NetworkDeviceDataMaxIPs, func(address string, fldPath *field.Path) field.ErrorList { return validation.IsValidInterfaceAddress(fldPath, address) - }, stringKey, fldPath.Child("ips"))...) + }, stringKey, fldPath.Child("ips"), sizeCovered, uniquenessCovered)...) return allErrs } @@ -1269,7 +1358,11 @@ func validateDeviceTaintRuleSpec(spec, oldSpec *resource.DeviceTaintRuleSpec, fl oldFilter = oldSpec.DeviceSelector // +k8s:verify-mutation:reason=clone } allErrs = append(allErrs, validateDeviceTaintSelector(spec.DeviceSelector, oldFilter, fldPath.Child("deviceSelector"))...) - allErrs = append(allErrs, validateDeviceTaint(spec.Taint, fldPath.Child("taint"))...) + var oldTaint *resource.DeviceTaint + if oldSpec != nil { + oldTaint = &oldSpec.Taint // +k8s:verify-mutation:reason=clone + } + allErrs = append(allErrs, validateDeviceTaint(spec.Taint, oldTaint, fldPath.Child("taint"))...) return allErrs } @@ -1279,9 +1372,6 @@ func validateDeviceTaintSelector(filter, oldFilter *resource.DeviceTaintSelector if filter == nil { return allErrs } - if filter.DeviceClassName != nil { - allErrs = append(allErrs, validateDeviceClassName(*filter.DeviceClassName, fldPath.Child("deviceClassName"))...) - } if filter.Driver != nil { allErrs = append(allErrs, validateDriverName(*filter.Driver, fldPath.Child("driver"))...) } @@ -1292,37 +1382,26 @@ func validateDeviceTaintSelector(filter, oldFilter *resource.DeviceTaintSelector allErrs = append(allErrs, validateDeviceName(*filter.Device, fldPath.Child("device"))...) } - // If the selectors are exactly as before, we treat the CEL expressions as "stored". - // Any change, including merely reordering selectors, triggers validation as new - // expressions. - stored := false - if oldFilter != nil { - stored = apiequality.Semantic.DeepEqual(filter.Selectors, oldFilter.Selectors) - } - allErrs = append(allErrs, validateSlice(filter.Selectors, resource.DeviceSelectorsMaxSize, - func(selector resource.DeviceSelector, fldPath *field.Path) field.ErrorList { - return validateSelector(selector, fldPath, stored) - }, - fldPath.Child("selectors"))...) - return allErrs } var validDeviceTolerationOperators = []resource.DeviceTolerationOperator{resource.DeviceTolerationOpEqual, resource.DeviceTolerationOpExists} -var validDeviceTaintEffects = sets.New(resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNoExecute) +var validDeviceTaintEffects = sets.New(resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNone) -func validateDeviceTaint(taint resource.DeviceTaint, fldPath *field.Path) field.ErrorList { +func validateDeviceTaint(taint resource.DeviceTaint, oldTaint *resource.DeviceTaint, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList allErrs = append(allErrs, metav1validation.ValidateLabelName(taint.Key, fldPath.Child("key"))...) // Includes checking for non-empty. if taint.Value != "" { allErrs = append(allErrs, validateLabelValue(taint.Value, fldPath.Child("value"))...) } - switch { - case taint.Effect == "": - allErrs = append(allErrs, field.Required(fldPath.Child("effect"), "")) // Required in a taint. - case !validDeviceTaintEffects.Has(taint.Effect): - allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), taint.Effect, sets.List(validDeviceTaintEffects))) + if oldTaint == nil || oldTaint.Effect != taint.Effect { + switch { + case taint.Effect == "": + allErrs = append(allErrs, field.Required(fldPath.Child("effect"), "").MarkCoveredByDeclarative()) // Required in a taint. + case !validDeviceTaintEffects.Has(taint.Effect): + allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), taint.Effect, sets.List(validDeviceTaintEffects)).MarkCoveredByDeclarative()) + } } return allErrs @@ -1332,7 +1411,7 @@ func validateDeviceToleration(toleration resource.DeviceToleration, fldPath *fie var allErrs field.ErrorList if toleration.Key != "" { - allErrs = append(allErrs, metav1validation.ValidateLabelName(toleration.Key, fldPath.Child("key"))...) + allErrs = append(allErrs, metav1validation.ValidateLabelName(toleration.Key, fldPath.Child("key")).MarkCoveredByDeclarative()...) } switch toleration.Operator { case resource.DeviceTolerationOpExists: @@ -1344,13 +1423,13 @@ func validateDeviceToleration(toleration resource.DeviceToleration, fldPath *fie case "": allErrs = append(allErrs, field.Required(fldPath.Child("operator"), "")) default: - allErrs = append(allErrs, field.NotSupported(fldPath.Child("operator"), toleration.Operator, validDeviceTolerationOperators)) + allErrs = append(allErrs, field.NotSupported(fldPath.Child("operator"), toleration.Operator, validDeviceTolerationOperators).MarkCoveredByDeclarative()) } switch { case toleration.Effect == "": // Optional in a toleration. case !validDeviceTaintEffects.Has(toleration.Effect): - allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), toleration.Effect, sets.List(validDeviceTaintEffects))) + allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), toleration.Effect, sets.List(validDeviceTaintEffects)).MarkCoveredByDeclarative()) } return allErrs @@ -1370,10 +1449,10 @@ func validateLabelValue(value string, fldPath *field.Path) field.ErrorList { func validateDeviceBindingParameters(bindingConditions, bindingFailureConditions []string, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList allErrs = append(allErrs, validateSlice(bindingConditions, resource.BindingConditionsMaxSize, - metav1validation.ValidateLabelName, fldPath.Child("bindingConditions"))...) + metav1validation.ValidateLabelName, fldPath.Child("bindingConditions"), sizeCovered)...) allErrs = append(allErrs, validateSlice(bindingFailureConditions, resource.BindingFailureConditionsMaxSize, - metav1validation.ValidateLabelName, fldPath.Child("bindingFailureConditions"))...) + metav1validation.ValidateLabelName, fldPath.Child("bindingFailureConditions"), sizeCovered)...) // ensure BindingConditions and BindingFailureConditions contain no duplicate items and do not overlap with each other conditionsSet := sets.New[string]() @@ -1405,3 +1484,17 @@ func validateDeviceBindingParameters(bindingConditions, bindingFailureConditions return allErrs } + +// ValidateDeviceTaintRuleStatusUpdate tests if a DeviceTaintRule status update is valid. +func ValidateDeviceTaintRuleStatusUpdate(rule, oldRule *resource.DeviceTaintRule) field.ErrorList { + var allErrs field.ErrorList + + fldPath := field.NewPath("status") + allErrs = corevalidation.ValidateObjectMetaUpdate(&rule.ObjectMeta, &oldRule.ObjectMeta, field.NewPath("metadata")) // Covers invalid name changes. + allErrs = append(allErrs, metav1validation.ValidateConditions(rule.Status.Conditions, fldPath.Child("conditions"))...) + if len(rule.Status.Conditions) > resource.DeviceTaintRuleStatusMaxConditions { + allErrs = append(allErrs, field.TooMany(fldPath.Child("conditions"), len(rule.Status.Conditions), resource.DeviceTaintRuleStatusMaxConditions)) + } + + return allErrs +} diff --git a/pkg/apis/resource/validation/validation_deviceclass_test.go b/pkg/apis/resource/validation/validation_deviceclass_test.go index 34de83bcbe935..1491a0920e01e 100644 --- a/pkg/apis/resource/validation/validation_deviceclass_test.go +++ b/pkg/apis/resource/validation/validation_deviceclass_test.go @@ -53,7 +53,7 @@ func TestValidateClass(t *testing.T) { class: testClass(""), }, "bad-name": { - wantFailures: field.ErrorList{field.Invalid(field.NewPath("metadata", "name"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, + wantFailures: field.ErrorList{field.Invalid(field.NewPath("metadata", "name"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')").MarkCoveredByDeclarative()}, class: testClass(badName), }, "generate-name": { @@ -181,7 +181,7 @@ func TestValidateClass(t *testing.T) { }(), }, "bad-extended-resource-name": { - wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "extendedResourceName"), "example.com", "must be a valid extended resource name")}, + wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "extendedResourceName"), "example.com", "must be a valid extended resource name").MarkCoveredByDeclarative().WithOrigin("format=k8s-extended-resource-name")}, class: func() *resource.DeviceClass { class := testClass(goodName) class.Spec.ExtendedResourceName = ptr.To(string("example.com")) @@ -219,7 +219,7 @@ func TestValidateClass(t *testing.T) { }, "too-many-selectors": { wantFailures: field.ErrorList{ - field.TooMany(field.NewPath("spec", "selectors"), resource.DeviceSelectorsMaxSize+1, resource.DeviceSelectorsMaxSize), + field.TooMany(field.NewPath("spec", "selectors"), resource.DeviceSelectorsMaxSize+1, resource.DeviceSelectorsMaxSize).WithOrigin("maxItems").MarkCoveredByDeclarative(), }, class: func() *resource.DeviceClass { class := testClass(goodName) @@ -236,8 +236,7 @@ func TestValidateClass(t *testing.T) { }, "configuration": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "config").Index(1).Child("opaque", "driver"), ""), - field.Invalid(field.NewPath("spec", "config").Index(1).Child("opaque", "driver"), "", "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"), + field.Required(field.NewPath("spec", "config").Index(1).Child("opaque", "driver"), "").MarkCoveredByDeclarative(), field.Required(field.NewPath("spec", "config").Index(1).Child("opaque", "parameters"), ""), field.Invalid(field.NewPath("spec", "config").Index(2).Child("opaque", "parameters"), "", "error parsing data as JSON: invalid character 'x' looking for beginning of value"), field.Invalid(field.NewPath("spec", "config").Index(3).Child("opaque", "parameters"), "", "must be a valid JSON object"), @@ -314,7 +313,7 @@ func TestValidateClass(t *testing.T) { }, "too-many-configs": { wantFailures: field.ErrorList{ - field.TooMany(field.NewPath("spec", "config"), resource.DeviceConfigMaxSize+1, resource.DeviceConfigMaxSize), + field.TooMany(field.NewPath("spec", "config"), resource.DeviceConfigMaxSize+1, resource.DeviceConfigMaxSize).WithOrigin("maxItems").MarkCoveredByDeclarative(), }, class: func() *resource.DeviceClass { class := testClass(goodName) diff --git a/pkg/apis/resource/validation/validation_devicetaintrule_test.go b/pkg/apis/resource/validation/validation_devicetaintrule_test.go index 9851c272bbcf3..815b6c515e4d4 100644 --- a/pkg/apis/resource/validation/validation_devicetaintrule_test.go +++ b/pkg/apis/resource/validation/validation_devicetaintrule_test.go @@ -17,7 +17,6 @@ limitations under the License. package validation import ( - "strings" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,10 +36,9 @@ func testDeviceTaintRule(name string, spec resourceapi.DeviceTaintRuleSpec) *res var validDeviceTaintRuleSpec = resourceapi.DeviceTaintRuleSpec{ DeviceSelector: &resourceapi.DeviceTaintSelector{ - DeviceClassName: ptr.To(goodName), - Driver: ptr.To("test.example.com"), - Pool: ptr.To(goodName), - Device: ptr.To(goodName), + Driver: ptr.To("test.example.com"), + Pool: ptr.To(goodName), + Device: ptr.To(goodName), }, Taint: resourceapi.DeviceTaint{ Key: "example.com/taint", @@ -187,14 +185,6 @@ func TestValidateDeviceTaint(t *testing.T) { return taintRule }(), }, - "bad-class": { - wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "deviceSelector", "deviceClassName"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, - taintRule: func() *resourceapi.DeviceTaintRule { - taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) - taintRule.Spec.DeviceSelector.DeviceClassName = ptr.To(badName) - return taintRule - }(), - }, "bad-driver": { wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "deviceSelector", "driver"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, taintRule: func() *resourceapi.DeviceTaintRule { @@ -219,91 +209,41 @@ func TestValidateDeviceTaint(t *testing.T) { return taintRule }(), }, - "CEL-compile-errors": { - wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "deviceSelector", "selectors").Index(1).Child("cel", "expression"), `device.attributes[true].someBoolean`, "compilation failed: ERROR: :1:18: found no matching overload for '_[_]' applied to '(map(string, map(string, any)), bool)'\n | device.attributes[true].someBoolean\n | .................^"), - }, - taintRule: func() *resourceapi.DeviceTaintRule { - taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) - taintRule.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{ - { - // Good selector. - CEL: &resourceapi.CELDeviceSelector{ - Expression: `device.driver == "dra.example.com"`, - }, - }, - { - // Bad selector. - CEL: &resourceapi.CELDeviceSelector{ - Expression: `device.attributes[true].someBoolean`, - }, - }, - } - return taintRule - }(), - }, - "CEL-length": { - wantFailures: field.ErrorList{ - field.TooLong(field.NewPath("spec", "deviceSelector", "selectors").Index(1).Child("cel", "expression"), "" /*unused*/, resourceapi.CELSelectorExpressionMaxLength), - }, - taintRule: func() *resourceapi.DeviceTaintRule { - taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) - expression := `device.driver == ""` - taintRule.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{ - { - // Good selector. - CEL: &resourceapi.CELDeviceSelector{ - Expression: strings.ReplaceAll(expression, `""`, `"`+strings.Repeat("x", resourceapi.CELSelectorExpressionMaxLength-len(expression))+`"`), - }, - }, - { - // Too long by one selector. - CEL: &resourceapi.CELDeviceSelector{ - Expression: strings.ReplaceAll(expression, `""`, `"`+strings.Repeat("x", resourceapi.CELSelectorExpressionMaxLength-len(expression)+1)+`"`), - }, - }, - } - return taintRule - }(), - }, - "CEL-cost": { - wantFailures: field.ErrorList{ - field.Forbidden(field.NewPath("spec", "deviceSelector", "selectors").Index(0).Child("cel", "expression"), "too complex, exceeds cost limit"), - }, + // Minimal tests for DeviceTaint. Full coverage of validateDeviceTaint is in ResourceSlice test. + "valid-taint": { taintRule: func() *resourceapi.DeviceTaintRule { claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) - claim.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{ - { - CEL: &resourceapi.CELDeviceSelector{ - // From https://github.com/kubernetes/kubernetes/blob/50fc400f178d2078d0ca46aee955ee26375fc437/test/integration/apiserver/cel/validatingadmissionpolicy_test.go#L2150. - Expression: `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(x, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(y, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z5, int('1'.find('[0-9]*')) < 100)))))))`, - }, - }, + claim.Spec.Taint = resourceapi.DeviceTaint{ + Key: goodName, + Value: goodName, + Effect: resourceapi.DeviceTaintEffectNoExecute, } return claim }(), }, - "valid-taint": { + "required-taint": { + wantFailures: field.ErrorList{ + field.Required(field.NewPath("spec", "taint", "effect"), "").MarkCoveredByDeclarative(), + }, taintRule: func() *resourceapi.DeviceTaintRule { claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) claim.Spec.Taint = resourceapi.DeviceTaint{ - Key: goodName, - Value: goodName, - Effect: resourceapi.DeviceTaintEffectNoExecute, + Key: goodName, + Value: goodName, } return claim }(), }, "invalid-taint": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "taint", "effect"), ""), + field.NotSupported(field.NewPath("spec", "taint", "effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone}).MarkCoveredByDeclarative(), }, taintRule: func() *resourceapi.DeviceTaintRule { claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec) claim.Spec.Taint = resourceapi.DeviceTaint{ - // Minimal test. Full coverage of validateDeviceTaint is in ResourceSlice test. - Key: goodName, - Value: goodName, + Effect: "some-other-effect", + Key: goodName, + Value: goodName, } return claim }(), @@ -321,6 +261,8 @@ func TestValidateDeviceTaint(t *testing.T) { func TestValidateDeviceTaintUpdate(t *testing.T) { name := "valid" validTaintRule := testDeviceTaintRule(name, validDeviceTaintRuleSpec) + invalidTaintEffectRule := validTaintRule.DeepCopy() + invalidTaintEffectRule.Spec.Taint.Effect = "some-other-effect" scenarios := map[string]struct { old *resourceapi.DeviceTaintRule @@ -339,6 +281,21 @@ func TestValidateDeviceTaintUpdate(t *testing.T) { return taintRule }, }, + "valid-existing-unknown-effect": { + old: invalidTaintEffectRule, + update: func(taintRule *resourceapi.DeviceTaintRule) *resourceapi.DeviceTaintRule { + taintRule.Labels = map[string]string{"a": "b"} + return taintRule + }, + }, + "invalid-new-unknown-effect": { + wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "taint", "effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(), + old: validTaintRule, + update: func(taintRule *resourceapi.DeviceTaintRule) *resourceapi.DeviceTaintRule { + taintRule.Spec.Taint.Effect = "some-other-effect" + return taintRule + }, + }, } for name, scenario := range scenarios { diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index 96100b328d8a8..9a6cffb82598c 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -257,19 +257,51 @@ func TestValidateClaim(t *testing.T) { return claim }(), }, - "invalid-request": { + "invalid-request-without-max-size-short-circuit": { wantFailures: field.ErrorList{ - field.TooMany(field.NewPath("spec", "devices", "requests"), resource.DeviceRequestsMaxSize+1, resource.DeviceRequestsMaxSize), field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"), - field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"), - field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain"), - field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "name required"), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(), field.Required(field.NewPath("spec", "devices", "constraints").Index(2).Child("matchAttribute"), ""), - field.TooMany(field.NewPath("spec", "devices", "constraints"), resource.DeviceConstraintsMaxSize+1, resource.DeviceConstraintsMaxSize), field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"), - field.TooMany(field.NewPath("spec", "devices", "config"), resource.DeviceConfigMaxSize+1, resource.DeviceConfigMaxSize), + }, + claim: func() *resource.ResourceClaim { + claim := testClaim(goodName, goodNS, validClaimSpec) + claim.Spec.Devices.Constraints = []resource.DeviceConstraint{ + { + Requests: []string{claim.Spec.Devices.Requests[0].Name, badName}, + MatchAttribute: ptr.To(resource.FullyQualifiedName("missing-domain")), + }, + { + MatchAttribute: ptr.To(resource.FullyQualifiedName("")), + }, + { + MatchAttribute: nil, + }, + } + claim.Spec.Devices.Config = []resource.DeviceClaimConfiguration{{ + Requests: []string{claim.Spec.Devices.Requests[0].Name, badName}, + DeviceConfiguration: resource.DeviceConfiguration{ + Opaque: &resource.OpaqueDeviceConfiguration{ + Driver: "dra.example.com", + Parameters: runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + }, + }, + }} + return claim + }(), + }, + "invalid-request-with-max-size-short-circuit": { + wantFailures: field.ErrorList{ + field.TooMany(field.NewPath("spec", "devices", "requests"), resource.DeviceRequestsMaxSize+1, resource.DeviceRequestsMaxSize).MarkCoveredByDeclarative(), + field.TooMany(field.NewPath("spec", "devices", "constraints"), resource.DeviceConstraintsMaxSize+1, resource.DeviceConstraintsMaxSize).MarkCoveredByDeclarative(), + field.TooMany(field.NewPath("spec", "devices", "config"), resource.DeviceConfigMaxSize+1, resource.DeviceConfigMaxSize).MarkCoveredByDeclarative(), }, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpec) @@ -322,8 +354,9 @@ func TestValidateClaim(t *testing.T) { "invalid-distinct-constraint": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"), - field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain"), - field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), "name required"), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash"), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash"), field.Required(field.NewPath("spec", "devices", "constraints").Index(2), `exactly one of "matchAttribute" or "distinctAttribute" is required, but multiple fields are set`)}, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpec) @@ -374,9 +407,10 @@ func TestValidateClaim(t *testing.T) { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"), - field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"), - field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain"), - field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "name required"), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(), field.Required(field.NewPath("spec", "devices", "constraints").Index(2).Child("matchAttribute"), ""), field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"), @@ -412,9 +446,9 @@ func TestValidateClaim(t *testing.T) { "allocation-mode": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices", "requests").Index(2).Child("exactly", "count"), int64(-1), "must be greater than zero"), - field.NotSupported(field.NewPath("spec", "devices", "requests").Index(3).Child("exactly", "allocationMode"), resource.DeviceAllocationMode("other"), []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount}), + field.NotSupported(field.NewPath("spec", "devices", "requests").Index(3).Child("exactly", "allocationMode"), resource.DeviceAllocationMode("other"), []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount}).MarkCoveredByDeclarative(), field.Invalid(field.NewPath("spec", "devices", "requests").Index(4).Child("exactly", "count"), int64(2), "must not be specified when allocationMode is 'All'"), - field.Duplicate(field.NewPath("spec", "devices", "requests").Index(5).Child("name"), "foo"), + field.Duplicate(field.NewPath("spec", "devices", "requests").Index(5), "foo").MarkCoveredByDeclarative(), }, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpec) @@ -622,11 +656,21 @@ func TestValidateClaim(t *testing.T) { return claim }(), }, + "first-available-with-zero-exactly-fails-old-validation": { + wantFailures: field.ErrorList{ + field.Invalid(field.NewPath("spec", "devices", "requests").Index(0), nil, "exactly one of `exactly` or `firstAvailable` is required, but multiple fields are set"), + }, + claim: func() *resource.ResourceClaim { + claim := testClaim(goodName, goodNS, validClaimSpecWithFirstAvailable) + claim.Spec.Devices.Requests[0].Exactly = &resource.ExactDeviceRequest{} + return claim + }(), + }, "prioritized-list-invalid-nested-request": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), - field.Required(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("deviceClassName"), ""), - field.NotSupported(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("allocationMode"), resource.DeviceAllocationMode(""), []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount}), + field.Required(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("deviceClassName"), "").MarkCoveredByDeclarative(), + field.NotSupported(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("allocationMode"), resource.DeviceAllocationMode(""), []resource.DeviceAllocationMode{resource.DeviceAllocationModeAll, resource.DeviceAllocationModeExactCount}).MarkCoveredByDeclarative(), }, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpecWithFirstAvailable) @@ -638,7 +682,7 @@ func TestValidateClaim(t *testing.T) { }, "prioritized-list-nested-requests-same-name": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(1).Child("name"), "foo"), + field.Duplicate(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable").Index(1), "foo").MarkCoveredByDeclarative(), }, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpecWithFirstAvailable) @@ -648,7 +692,7 @@ func TestValidateClaim(t *testing.T) { }, "prioritized-list-too-many-subrequests": { wantFailures: field.ErrorList{ - field.TooMany(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable"), 9, 8), + field.TooMany(field.NewPath("spec", "devices", "requests").Index(0).Child("firstAvailable"), 9, 8).MarkCoveredByDeclarative(), }, claim: func() *resource.ResourceClaim { claim := testClaim(goodName, goodNS, validClaimSpec) @@ -776,11 +820,11 @@ func TestValidateClaim(t *testing.T) { allErrs = append(allErrs, field.Required(fldPath.Index(3).Child("operator"), ""), - field.NotSupported(fldPath.Index(4).Child("operator"), resource.DeviceTolerationOperator("some-other-op"), []resource.DeviceTolerationOperator{resource.DeviceTolerationOpEqual, resource.DeviceTolerationOpExists}), + field.NotSupported(fldPath.Index(4).Child("operator"), resource.DeviceTolerationOperator("some-other-op"), []resource.DeviceTolerationOperator{resource.DeviceTolerationOpEqual, resource.DeviceTolerationOpExists}).MarkCoveredByDeclarative(), - field.Invalid(fldPath.Index(5).Child("key"), badName, "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"), + field.Invalid(fldPath.Index(5).Child("key"), badName, "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')").MarkCoveredByDeclarative(), field.Invalid(fldPath.Index(5).Child("value"), badName, "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')"), - field.NotSupported(fldPath.Index(5).Child("effect"), resource.DeviceTaintEffect("some-other-effect"), []resource.DeviceTaintEffect{resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNoSchedule}), + field.NotSupported(fldPath.Index(5).Child("effect"), resource.DeviceTaintEffect("some-other-effect"), []resource.DeviceTaintEffect{resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNone}).MarkCoveredByDeclarative(), ) return allErrs }(), @@ -855,7 +899,7 @@ func TestValidateClaimUpdate(t *testing.T) { spec := validClaim.Spec.DeepCopy() spec.Devices.Requests[0].Exactly.DeviceClassName += "2" return *spec - }(), "field is immutable")}, + }(), "field is immutable")}.MarkCoveredByDeclarative(), oldClaim: validClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { claim.Spec.Devices.Requests[0].Exactly.DeviceClassName += "2" @@ -1049,7 +1093,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, }, "invalid-reserved-for-too-large": { - wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "reservedFor"), resource.ResourceClaimReservedForMaxSize+1, resource.ResourceClaimReservedForMaxSize)}, + wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "reservedFor"), resource.ResourceClaimReservedForMaxSize+1, resource.ResourceClaimReservedForMaxSize).MarkCoveredByDeclarative()}, oldClaim: validAllocatedClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { for i := 0; i < resource.ResourceClaimReservedForMaxSize+1; i++ { @@ -1064,7 +1108,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, }, "invalid-reserved-for-duplicate": { - wantFailures: field.ErrorList{field.Duplicate(field.NewPath("status", "reservedFor").Index(1).Child("uid"), types.UID("1"))}, + wantFailures: field.ErrorList{field.Duplicate(field.NewPath("status", "reservedFor").Index(1), types.UID("1")).MarkCoveredByDeclarative()}, oldClaim: validAllocatedClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { for i := 0; i < 2; i++ { @@ -1155,7 +1199,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { claim := validAllocatedClaim.DeepCopy() claim.Status.Allocation.Devices.Results[0].Driver += "-2" return claim.Status.Allocation - }(), "field is immutable")}, + }(), "field is immutable").MarkCoveredByDeclarative()}, oldClaim: validAllocatedClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { claim.Status.Allocation.Devices.Results[0].Driver += "-2" @@ -1186,13 +1230,35 @@ func TestValidateClaimStatusUpdate(t *testing.T) { return claim }, }, + "invalid-duplicate-request-name": { + wantFailures: field.ErrorList{ + field.Duplicate(field.NewPath("status", "allocation", "devices", "config").Index(0).Child("requests").Index(1), "foo").MarkCoveredByDeclarative(), + }, + oldClaim: validClaim, + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim = claim.DeepCopy() + claim.Status.Allocation = validAllocatedClaim.Status.Allocation.DeepCopy() + claim.Status.Allocation.Devices.Config = []resource.DeviceAllocationConfiguration{{ + Source: resource.AllocationConfigSourceClaim, + Requests: []string{claim.Spec.Devices.Requests[0].Name, claim.Spec.Devices.Requests[0].Name}, + DeviceConfiguration: resource.DeviceConfiguration{ + Opaque: &resource.OpaqueDeviceConfiguration{ + Driver: "dra.example.com", + Parameters: runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + }, + }, + }} + return claim + }, + }, "configuration": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("status", "allocation", "devices", "config").Index(1).Child("source"), ""), - field.NotSupported(field.NewPath("status", "allocation", "devices", "config").Index(2).Child("source"), resource.AllocationConfigSource("no-such-source"), []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}), + field.Required(field.NewPath("status", "allocation", "devices", "config").Index(1).Child("source"), "").MarkCoveredByDeclarative(), + field.NotSupported(field.NewPath("status", "allocation", "devices", "config").Index(2).Child("source"), resource.AllocationConfigSource("no-such-source"), []resource.AllocationConfigSource{resource.AllocationConfigSourceClaim, resource.AllocationConfigSourceClass}).MarkCoveredByDeclarative(), field.Required(field.NewPath("status", "allocation", "devices", "config").Index(3).Child("opaque"), ""), - field.Required(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "driver"), ""), - field.Invalid(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "driver"), "", "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"), + field.Required(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "driver"), "").MarkCoveredByDeclarative(), field.Required(field.NewPath("status", "allocation", "devices", "config").Index(4).Child("opaque", "parameters"), ""), field.TooLong(field.NewPath("status", "allocation", "devices", "config").Index(6).Child("opaque", "parameters"), "" /* unused */, resource.OpaqueParametersMaxLength), }, @@ -1330,8 +1396,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-device-status-duplicate": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64"), - field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), nil)), + field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64").MarkCoveredByDeclarative(), + field.Duplicate(field.NewPath("status", "devices").Index(1), structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), nil)).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -1359,8 +1425,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-device-status-duplicate-with-share-id": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), - structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), ptr.To(goodShareID))), + field.Duplicate(field.NewPath("status", "devices").Index(1), + structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), ptr.To(goodShareID))).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { claim := validAllocatedClaim.DeepCopy() @@ -1389,8 +1455,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-network-device-status": { wantFailures: field.ErrorList{ - field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", resource.NetworkDeviceDataInterfaceNameMaxLength), - field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", resource.NetworkDeviceDataHardwareAddressMaxLength), + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", resource.NetworkDeviceDataInterfaceNameMaxLength).MarkCoveredByDeclarative(), + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", resource.NetworkDeviceDataHardwareAddressMaxLength).MarkCoveredByDeclarative(), field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(0), "300.9.8.0/24", "must be a valid address in CIDR form, (e.g. 10.9.8.7/24 or 2001:db8::1/64)"), field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "010.009.008.000/24", "must be in canonical form (\"10.9.8.0/24\")"), field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(2), "2001:0db8::1/64", "must be in canonical form (\"2001:db8::1/64\")"), @@ -1441,7 +1507,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { wantFailures: field.ErrorList{ field.TooMany(field.NewPath("status", "devices").Index(0).Child("conditions"), resource.AllocatedDeviceStatusMaxConditions+1, resource.AllocatedDeviceStatusMaxConditions), field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.AllocatedDeviceStatusDataMaxLength), - field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), resource.NetworkDeviceDataMaxIPs+1, resource.NetworkDeviceDataMaxIPs), + field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), resource.NetworkDeviceDataMaxIPs+1, resource.NetworkDeviceDataMaxIPs).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -1493,8 +1559,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-device-status-duplicate-disabled-feature-gate": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64"), - field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), nil)), + field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64").MarkCoveredByDeclarative(), + field.Duplicate(field.NewPath("status", "devices").Index(1), structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), nil)).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -1522,8 +1588,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-device-status-duplicate-with-share-id-disabled-feature-gate": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), - structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), ptr.To(goodShareID))), + field.Duplicate(field.NewPath("status", "devices").Index(1), + structured.MakeSharedDeviceID(structured.MakeDeviceID(goodName, goodName, goodName), ptr.To(goodShareID))).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { claim := validAllocatedClaim.DeepCopy() @@ -1552,8 +1618,8 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-network-device-status-disabled-feature-gate": { wantFailures: field.ErrorList{ - field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", resource.NetworkDeviceDataInterfaceNameMaxLength), - field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", resource.NetworkDeviceDataHardwareAddressMaxLength), + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", resource.NetworkDeviceDataInterfaceNameMaxLength).MarkCoveredByDeclarative(), + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", resource.NetworkDeviceDataHardwareAddressMaxLength).MarkCoveredByDeclarative(), field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(0), "300.9.8.0/24", "must be a valid address in CIDR form, (e.g. 10.9.8.7/24 or 2001:db8::1/64)"), }, oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), @@ -1600,7 +1666,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { wantFailures: field.ErrorList{ field.TooMany(field.NewPath("status", "devices").Index(0).Child("conditions"), resource.AllocatedDeviceStatusMaxConditions+1, resource.AllocatedDeviceStatusMaxConditions), field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.AllocatedDeviceStatusDataMaxLength), - field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), resource.NetworkDeviceDataMaxIPs+1, resource.NetworkDeviceDataMaxIPs), + field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), resource.NetworkDeviceDataMaxIPs+1, resource.NetworkDeviceDataMaxIPs).MarkCoveredByDeclarative(), }, oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -1819,7 +1885,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, }, "too-many-binding-conditions": { - wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("bindingConditions"), 5, 4)}, + wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("bindingConditions"), 5, 4).MarkCoveredByDeclarative()}, oldClaim: validClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { claim.Status.Allocation = &resource.AllocationResult{ @@ -1839,7 +1905,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, }, "too-many-binding-failure-conditions": { - wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("bindingFailureConditions"), 5, 4)}, + wantFailures: field.ErrorList{field.TooMany(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("bindingFailureConditions"), 5, 4).MarkCoveredByDeclarative()}, oldClaim: validClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { claim.Status.Allocation = &resource.AllocationResult{ @@ -2158,10 +2224,10 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, "invalid-add-allocated-status-invalid-share-id": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("status", "devices").Index(0).Child("shareID"), badLengthShareIDStr, "error validating uid: invalid UUID length: 1"), - field.Invalid(field.NewPath("status", "devices").Index(1).Child("shareID"), badFormatShareIDStr, "uid must be in RFC 4122 normalized form, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with lowercase hexadecimal characters"), - field.Invalid(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("shareID"), badLengthShareIDStr, "error validating uid: invalid UUID length: 1"), - field.Invalid(field.NewPath("status", "allocation", "devices", "results").Index(1).Child("shareID"), badFormatShareIDStr, "uid must be in RFC 4122 normalized form, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with lowercase hexadecimal characters"), + field.Invalid(field.NewPath("status", "devices").Index(0).Child("shareID"), badLengthShareIDStr, "error validating uid: invalid UUID length: 1").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("status", "devices").Index(1).Child("shareID"), badFormatShareIDStr, "uid must be in RFC 4122 normalized form, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with lowercase hexadecimal characters").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("shareID"), badLengthShareIDStr, "error validating uid: invalid UUID length: 1").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("status", "allocation", "devices", "results").Index(1).Child("shareID"), badFormatShareIDStr, "uid must be in RFC 4122 normalized form, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` with lowercase hexadecimal characters").MarkCoveredByDeclarative(), }, oldClaim: testClaim(goodName, goodNS, validClaimSpec), update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -2240,10 +2306,12 @@ func TestValidateClaimStatusUpdate(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, scenario.adminAccess) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, scenario.deviceStatusFeatureGate) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAPrioritizedList, scenario.prioritizedListFeatureGate) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAConsumableCapacity, scenario.consumableCapacityFeatureGate) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.DRAAdminAccess: scenario.adminAccess, + features.DRAResourceClaimDeviceStatus: scenario.deviceStatusFeatureGate, + features.DRAPrioritizedList: scenario.prioritizedListFeatureGate, + features.DRAConsumableCapacity: scenario.consumableCapacityFeatureGate, + }) scenario.oldClaim.ResourceVersion = "1" errs := ValidateResourceClaimStatusUpdate(scenario.update(scenario.oldClaim.DeepCopy()), scenario.oldClaim) diff --git a/pkg/apis/resource/validation/validation_resourceclaimtemplate_test.go b/pkg/apis/resource/validation/validation_resourceclaimtemplate_test.go index a35e02882a31a..7cf5b185064a8 100644 --- a/pkg/apis/resource/validation/validation_resourceclaimtemplate_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaimtemplate_test.go @@ -203,7 +203,7 @@ func TestValidateClaimTemplate(t *testing.T) { }(), }, "prioritized-list-bad-class-name-on-subrequest": { - wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("deviceClassName"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, + wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "spec", "devices", "requests").Index(0).Child("firstAvailable").Index(0).Child("deviceClassName"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')").MarkCoveredByDeclarative()}, template: func() *resource.ResourceClaimTemplate { template := testClaimTemplate(goodName, goodNS, validClaimSpecWithFirstAvailable) template.Spec.Spec.Devices.Requests[0].FirstAvailable[0].DeviceClassName = badName diff --git a/pkg/apis/resource/validation/validation_resourceslice_test.go b/pkg/apis/resource/validation/validation_resourceslice_test.go index 7669cf15fa4c1..7a867b6bdd538 100644 --- a/pkg/apis/resource/validation/validation_resourceslice_test.go +++ b/pkg/apis/resource/validation/validation_resourceslice_test.go @@ -55,6 +55,14 @@ func testCounters() map[string]resourceapi.Counter { } } +func testMultipleCounters(count int) map[string]resourceapi.Counter { + counters := make(map[string]resourceapi.Counter) + for i := 0; i < count; i++ { + counters[fmt.Sprintf("memory-%d", i)] = resourceapi.Counter{Value: resource.MustParse("1Gi")} + } + return counters +} + func testResourceSlice(name, nodeName, driverName string, numDevices int) *resourceapi.ResourceSlice { slice := &resourceapi.ResourceSlice{ ObjectMeta: metav1.ObjectMeta{ @@ -80,6 +88,19 @@ func testResourceSlice(name, nodeName, driverName string, numDevices int) *resou return slice } +func testResourceSliceWithSharedCounters(name, poolName, driverName string, numCounterSets int) *resourceapi.ResourceSlice { + slice := testResourceSlice(name, poolName, driverName, 1) + slice.Spec.Devices = nil + for i := 0; i < numCounterSets; i++ { + counterSet := resourceapi.CounterSet{ + Name: fmt.Sprintf("counterset-%d", i), + Counters: testCounters(), + } + slice.Spec.SharedCounters = append(slice.Spec.SharedCounters, counterSet) + } + return slice +} + func testResourceSliceWithBindingConditions(name, nodeName, driverName string, numDevices int, bindingConditions, bindingFailureConditions []string) *resourceapi.ResourceSlice { slice := testResourceSlice(name, nodeName, driverName, numDevices) for i := range slice.Spec.Devices { @@ -108,6 +129,25 @@ func TestValidateResourceSlice(t *testing.T) { wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevices+1, resourceapi.ResourceSliceMaxDevices)}, slice: testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevices+1), }, + "good-taints": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters) + for i := range slice.Spec.Devices { + slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}} + } + return slice + }(), + }, + "too-large-taints": { + wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)}, + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1) + for i := range slice.Spec.Devices { + slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}} + } + return slice + }(), + }, "missing-name": { wantFailures: field.ErrorList{field.Required(field.NewPath("metadata", "name"), "name or generateName is required")}, slice: testResourceSlice("", goodName, driverName, 1), @@ -323,6 +363,21 @@ func TestValidateResourceSlice(t *testing.T) { wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "driver"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")}, slice: testResourceSlice(goodName, goodName, badName, 1), }, + "both-shared-counters-and-devices": { + wantFailures: field.ErrorList{ + field.Invalid(field.NewPath("spec"), "", "only one of `sharedCounters` or `devices` is allowed"), + }, + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters = []resourceapi.CounterSet{ + { + Name: "counterset", + Counters: testCounters(), + }, + } + return slice + }(), + }, "bad-devices": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices").Index(1).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), @@ -336,8 +391,8 @@ func TestValidateResourceSlice(t *testing.T) { "bad-attribute": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("spec", "devices").Index(1).Child("attributes").Key(badName), badName, "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"), - field.Required(field.NewPath("spec", "devices").Index(1).Child("attributes").Key(badName), "exactly one value must be specified"), - field.Invalid(field.NewPath("spec", "devices").Index(2).Child("attributes").Key(goodName), resourceapi.DeviceAttribute{StringValue: ptr.To("x"), VersionValue: ptr.To("1.2.3")}, "exactly one value must be specified"), + field.Invalid(field.NewPath("spec", "devices").Index(1).Child("attributes").Key(badName), "", "exactly one value must be specified").MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("spec", "devices").Index(2).Child("attributes").Key(goodName), resourceapi.DeviceAttribute{StringValue: ptr.To("x"), VersionValue: ptr.To("1.2.3")}, "exactly one value must be specified").MarkCoveredByDeclarative(), field.Invalid(field.NewPath("spec", "devices").Index(3).Child("attributes").Key(goodName).Child("version"), strings.Repeat("x", resourceapi.DeviceAttributeMaxValueLength+1), "must be a string compatible with semver.org spec 2.0.0"), field.TooLongMaxLength(field.NewPath("spec", "devices").Index(3).Child("attributes").Key(goodName).Child("version"), strings.Repeat("x", resourceapi.DeviceAttributeMaxValueLength+1), resourceapi.DeviceAttributeMaxValueLength), field.TooLongMaxLength(field.NewPath("spec", "devices").Index(4).Child("attributes").Key(goodName).Child("string"), strings.Repeat("x", resourceapi.DeviceAttributeMaxValueLength+1), resourceapi.DeviceAttributeMaxValueLength), @@ -410,8 +465,8 @@ func TestValidateResourceSlice(t *testing.T) { }, "bad-attribute-empty-domain-and-c-identifier": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "devices").Index(1).Child("attributes").Key("/"), "the domain must not be empty"), - field.Required(field.NewPath("spec", "devices").Index(1).Child("attributes").Key("/"), "the name must not be empty"), + field.Invalid(field.NewPath("spec", "devices").Index(1).Child("attributes").Key("/"), "", "the domain must not be empty"), + field.Invalid(field.NewPath("spec", "devices").Index(1).Child("attributes").Key("/"), "", "the name must not be empty"), }, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, goodName, 2) @@ -493,11 +548,11 @@ func TestValidateResourceSlice(t *testing.T) { return field.ErrorList{ field.Invalid(fldPath.Index(2).Child("key"), "", "name part must be non-empty"), field.Invalid(fldPath.Index(2).Child("key"), "", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"), - field.Required(fldPath.Index(2).Child("effect"), ""), + field.Required(fldPath.Index(2).Child("effect"), "").MarkCoveredByDeclarative(), field.Invalid(fldPath.Index(3).Child("key"), badName, "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"), field.Invalid(fldPath.Index(3).Child("value"), badName, "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')"), - field.NotSupported(fldPath.Index(3).Child("effect"), resourceapi.DeviceTaintEffect("some-other-op"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule}), + field.NotSupported(fldPath.Index(3).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone}).MarkCoveredByDeclarative(), } }(), slice: func() *resourceapi.ResourceSlice { @@ -522,7 +577,7 @@ func TestValidateResourceSlice(t *testing.T) { // Invalid strings. Key: badName, Value: badName, - Effect: "some-other-op", + Effect: "some-other-effect", }, } return slice @@ -646,47 +701,61 @@ func TestValidateResourceSlice(t *testing.T) { return slice }(), }, + "missing-name-shared-counters": { + wantFailures: field.ErrorList{ + field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), "").MarkCoveredByDeclarative(), + }, + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters = []resourceapi.CounterSet{ + { + Counters: testCounters(), + }, + } + return slice + }(), + }, "bad-name-shared-counters": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), - field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("counters"), ""), + field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')").MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 1) + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 0) slice.Spec.SharedCounters = []resourceapi.CounterSet{ { - Name: badName, + Name: badName, + Counters: testCounters(), }, } return slice }(), }, - "bad-countername-shared-counters": { + "missing-counter-shared-counters": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("counters").Key(badName), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), + field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("counters"), ""), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 1) + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 0) slice.Spec.SharedCounters = []resourceapi.CounterSet{ { Name: goodName, - Counters: map[string]resourceapi.Counter{ - badName: {Value: resource.MustParse("1Gi")}, - }, }, } return slice }(), }, - "missing-name-shared-counters": { + "bad-countername-shared-counters": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), ""), + field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("counters").Key(badName), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 1) + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) slice.Spec.SharedCounters = []resourceapi.CounterSet{ { - Counters: testCounters(), + Name: goodName, + Counters: map[string]resourceapi.Counter{ + badName: {Value: resource.MustParse("1Gi")}, + }, }, } return slice @@ -694,10 +763,10 @@ func TestValidateResourceSlice(t *testing.T) { }, "duplicate-shared-counters": { wantFailures: field.ErrorList{ - field.Duplicate(field.NewPath("spec", "sharedCounters").Index(1).Child("name"), goodName), + field.Duplicate(field.NewPath("spec", "sharedCounters").Index(1), goodName).MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 1) + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) slice.Spec.SharedCounters = []resourceapi.CounterSet{ { Name: goodName, @@ -711,24 +780,46 @@ func TestValidateResourceSlice(t *testing.T) { return slice }(), }, - "too-large-shared-counters": { + "max-shared-counters": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxCounterSets) + return slice + }(), + }, + "too-many-shared-counters": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxSharedCounters+1, fmt.Sprintf("the total number of shared counters must not exceed %d", resourceapi.ResourceSliceMaxSharedCounters)), + field.TooMany(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxCounterSets+1, resourceapi.ResourceSliceMaxCounterSets).MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxSharedCounters + 1) + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxCounterSets + 1) + return slice + }(), + }, + "max-counters-in-counter-set": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters[0].Counters = testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerCounterSet) + return slice + }(), + }, + "too-many-counters-in-counter-set": { + wantFailures: field.ErrorList{ + field.TooMany(field.NewPath("spec", "sharedCounters").Index(0).Child("counters"), resourceapi.ResourceSliceMaxCountersPerCounterSet+1, resourceapi.ResourceSliceMaxCountersPerCounterSet), + }, + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1) + slice.Spec.SharedCounters[0].Counters = testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerCounterSet + 1) return slice }(), }, - "missing-counterset-consumes-counter": { + "missing-name-counterset-consumes-counter": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), ""), - field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "", "must reference a counterSet defined in the ResourceSlice sharedCounters"), + field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "").MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(1) slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ { Counters: testCounters(), @@ -737,80 +828,129 @@ func TestValidateResourceSlice(t *testing.T) { return slice }(), }, - "missing-counter-consumes-counter": { + "bad-name-counterset-consumes-counter": { wantFailures: field.ErrorList{ - field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), ""), + field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')").MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(1) slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ { - CounterSet: "counterset-0", + CounterSet: badName, + Counters: testCounters(), }, } return slice }(), }, - "wrong-counterref-consumes-counter": { + "duplicate-counterset-consumes-counter": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), "fake", "must reference a counter defined in the ResourceSlice sharedCounters"), - }, + field.Duplicate(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(1), goodName).MarkCoveredByDeclarative()}, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(1) slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ { - Counters: map[string]resourceapi.Counter{ - "fake": {Value: resource.MustParse("1Gi")}, - }, - CounterSet: "counterset-0", + CounterSet: goodName, + Counters: testCounters(), + }, + { + CounterSet: goodName, + Counters: testCounters(), }, } return slice }(), }, - "wrong-sharedcounterref-consumes-counter": { + "missing-counter-consumes-counter": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "fake", "must reference a counterSet defined in the ResourceSlice sharedCounters"), + field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), ""), }, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(1) slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ { - Counters: testCounters(), - CounterSet: "fake", + CounterSet: "counterset-0", }, } return slice }(), }, - "too-large-consumes-counter": { + "max-device-counter-consumption-for-device": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, driverName, 1) + slice.Spec.Devices[0].Attributes = nil + slice.Spec.Devices[0].Capacity = nil + slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice) + return slice + }(), + }, + "too-many-device-counter-consumption-for-device": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "devices").Index(0), resourceapi.ResourceSliceMaxCountersPerDevice+1, fmt.Sprintf("the total number of counters must not exceed %d", resourceapi.ResourceSliceMaxCountersPerDevice)), - field.Invalid(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxSharedCounters+1, fmt.Sprintf("the total number of shared counters must not exceed %d", resourceapi.ResourceSliceMaxSharedCounters)), + field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters"), resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice+1, resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice).MarkCoveredByDeclarative(), }, slice: func() *resourceapi.ResourceSlice { slice := testResourceSlice(goodName, goodName, driverName, 1) - slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxSharedCounters + 1) slice.Spec.Devices[0].Attributes = nil slice.Spec.Devices[0].Capacity = nil - slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxCountersPerDevice + 1) + slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice + 1) return slice }(), }, - "too-many-device-counters-in-slice": { + "max-counters-in-device-counter-consumption": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, driverName, 1) + slice.Spec.Devices[0].Attributes = nil + slice.Spec.Devices[0].Capacity = nil + slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ + { + CounterSet: "counterset-0", + Counters: testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption), + }, + } + return slice + }(), + }, + "too-many-counters-in-device-counter-consumption": { wantFailures: field.ErrorList{ - field.Invalid(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDeviceCountersPerSlice+1, fmt.Sprintf("the total number of counters in devices must not exceed %d", resourceapi.ResourceSliceMaxDeviceCountersPerSlice)), + field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption+1, resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption), }, slice: func() *resourceapi.ResourceSlice { - slice := testResourceSlice(goodName, goodName, driverName, 65) - slice.Spec.SharedCounters = createSharedCounters(16) - for i := 0; i < 64; i++ { - slice.Spec.Devices[i].ConsumesCounters = createConsumesCounters(16) + slice := testResourceSlice(goodName, goodName, driverName, 1) + slice.Spec.Devices[0].Attributes = nil + slice.Spec.Devices[0].Capacity = nil + slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ + { + CounterSet: "counterset-0", + Counters: testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption + 1), + }, + } + return slice + }(), + }, + "max-number-of-devices-with-consumes-counters": { + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters) + for i := range slice.Spec.Devices { + slice.Spec.Devices[i].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ + { + CounterSet: "counterset-0", + Counters: testCounters(), + }, + } + } + return slice + }(), + }, + "too-many-devices-with-consumes-counters": { + wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)}, + slice: func() *resourceapi.ResourceSlice { + slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1) + slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{ + { + CounterSet: "counterset-0", + Counters: testCounters(), + }, } - slice.Spec.Devices[64].ConsumesCounters = createConsumesCounters(1) return slice }(), }, @@ -818,11 +958,11 @@ func TestValidateResourceSlice(t *testing.T) { slice: testResourceSliceWithBindingConditions(goodName, goodName, driverName, 1, []string{"example.com/condition1", "condition2"}, []string{"example.com/condition3", "condition4"}), }, "too-many-binding-conditions": { - wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingConditions"), resourceapi.BindingConditionsMaxSize+1, resourceapi.BindingConditionsMaxSize)}, + wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingConditions"), resourceapi.BindingConditionsMaxSize+1, resourceapi.BindingConditionsMaxSize).MarkCoveredByDeclarative()}, slice: testResourceSliceWithBindingConditions(goodName, goodName, driverName, 1, []string{"condition1", "condition2", "condition3", "condition4", "condition5"}, []string{"condition6", "condition7"}), }, "too-many-binding-failure-conditions": { - wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingFailureConditions"), resourceapi.BindingConditionsMaxSize+1, resourceapi.BindingConditionsMaxSize)}, + wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingFailureConditions"), resourceapi.BindingConditionsMaxSize+1, resourceapi.BindingConditionsMaxSize).MarkCoveredByDeclarative()}, slice: testResourceSliceWithBindingConditions(goodName, goodName, driverName, 1, []string{"condition1", "condition2"}, []string{"condition3", "condition4", "condition5", "condition6", "condition7"}), }, "invalid-binding-conditions": { @@ -867,6 +1007,17 @@ func TestValidateResourceSlice(t *testing.T) { func TestValidateResourceSliceUpdate(t *testing.T) { name := "valid" validResourceSlice := testResourceSlice(name, name, name, 1) + invalidResourceSliceWithTaints := validResourceSlice.DeepCopy() + invalidResourceSliceWithTaints.Spec.Devices[0].Taints = []resourceapi.DeviceTaint{ + { + Key: "unhealthy-power", + Effect: resourceapi.DeviceTaintEffectNoExecute, + }, + { + Key: "unhealthy-mem", + Effect: "some-other-effect", + }, + } scenarios := map[string]struct { oldResourceSlice *resourceapi.ResourceSlice @@ -938,6 +1089,31 @@ func TestValidateResourceSliceUpdate(t *testing.T) { return slice }, }, + "invalid-new-effect-in-old-device": { + wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "devices").Index(0).Child("taints").Index(1).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(), + oldResourceSlice: validResourceSlice, + update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice { + slice.Spec.Devices[0].Taints = invalidResourceSliceWithTaints.Spec.Devices[0].Taints + return slice + }, + }, + "valid-old-effect": { + oldResourceSlice: invalidResourceSliceWithTaints, + update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice { + slice.Spec.Devices[0].Attributes["foo"] = resourceapi.DeviceAttribute{StringValue: ptr.To("bar")} + return slice + }, + }, + "invalid-new-effect-in-new-device": { + wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "devices").Index(1).Child("taints").Index(1).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(), + oldResourceSlice: invalidResourceSliceWithTaints, + update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice { + device := slice.Spec.Devices[0].DeepCopy() + device.Name += "-other" + slice.Spec.Devices = append(slice.Spec.Devices, *device) + return slice + }, + }, } for name, scenario := range scenarios { diff --git a/pkg/apis/resource/zz_generated.deepcopy.go b/pkg/apis/resource/zz_generated.deepcopy.go index 937d5a5b35a3c..1b869906e0556 100644 --- a/pkg/apis/resource/zz_generated.deepcopy.go +++ b/pkg/apis/resource/zz_generated.deepcopy.go @@ -831,6 +831,7 @@ func (in *DeviceTaintRule) DeepCopyInto(out *DeviceTaintRule) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) return } @@ -908,13 +909,31 @@ func (in *DeviceTaintRuleSpec) DeepCopy() *DeviceTaintRuleSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) { +func (in *DeviceTaintRuleStatus) DeepCopyInto(out *DeviceTaintRuleStatus) { *out = *in - if in.DeviceClassName != nil { - in, out := &in.DeviceClassName, &out.DeviceClassName - *out = new(string) - **out = **in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceTaintRuleStatus. +func (in *DeviceTaintRuleStatus) DeepCopy() *DeviceTaintRuleStatus { + if in == nil { + return nil } + out := new(DeviceTaintRuleStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) { + *out = *in if in.Driver != nil { in, out := &in.Driver, &out.Driver *out = new(string) @@ -930,13 +949,6 @@ func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) { *out = new(string) **out = **in } - if in.Selectors != nil { - in, out := &in.Selectors, &out.Selectors - *out = make([]DeviceSelector, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } return } diff --git a/pkg/apis/scheduling/OWNERS b/pkg/apis/scheduling/OWNERS new file mode 100644 index 0000000000000..0c9596d13a877 --- /dev/null +++ b/pkg/apis/scheduling/OWNERS @@ -0,0 +1,7 @@ +# See the OWNERS docs at https://go.k8s.io/owners + +reviewers: + - sig-scheduling-maintainers + - sig-scheduling +labels: + - sig/scheduling diff --git a/pkg/apis/scheduling/register.go b/pkg/apis/scheduling/register.go index 664b5bd251261..ae4c71a6ca473 100644 --- a/pkg/apis/scheduling/register.go +++ b/pkg/apis/scheduling/register.go @@ -48,6 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &PriorityClass{}, &PriorityClassList{}, + &Workload{}, + &WorkloadList{}, ) return nil } diff --git a/pkg/apis/scheduling/types.go b/pkg/apis/scheduling/types.go index 68daf016811e7..12730404c5f76 100644 --- a/pkg/apis/scheduling/types.go +++ b/pkg/apis/scheduling/types.go @@ -88,3 +88,129 @@ type PriorityClassList struct { // items is the list of PriorityClasses. Items []PriorityClass } + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Workload allows for expressing scheduling constraints that should be used +// when managing lifecycle of workloads from scheduling perspective, +// including scheduling, preemption, eviction and other phases. +type Workload struct { + metav1.TypeMeta + // Standard object's metadata. + // Name must be a DNS subdomain. + // + // +optional + metav1.ObjectMeta + + // Spec defines the desired behavior of a Workload. + // + // +required + Spec WorkloadSpec +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkloadList contains a list of Workload resources. +type WorkloadList struct { + metav1.TypeMeta + // Standard list metadata. + // + // +optional + metav1.ListMeta + + // Items is the list of Workloads. + Items []Workload +} + +// WorkloadMaxPodGroups is the maximum number of pod groups per Workload. +const WorkloadMaxPodGroups = 8 + +// WorkloadSpec defines the desired state of a Workload. +type WorkloadSpec struct { + // ControllerRef is an optional reference to the controlling object, such as a + // Deployment or Job. This field is intended for use by tools like CLIs + // to provide a link back to the original workload definition. + // When set, it cannot be changed. + // + // +optional + ControllerRef *TypedLocalObjectReference + + // PodGroups is the list of pod groups that make up the Workload. + // The maximum number of pod groups is 8. This field is immutable. + // + // +required + // +listType=map + // +listMapKey=name + PodGroups []PodGroup +} + +// TypedLocalObjectReference allows to reference typed object inside the same namespace. +type TypedLocalObjectReference struct { + // APIGroup is the group for the resource being referenced. + // If APIGroup is empty, the specified Kind must be in the core API group. + // For any other third-party types, setting APIGroup is required. + // It must be a DNS subdomain. + // + // +optional + APIGroup string + // Kind is the type of resource being referenced. + // It must be a path segment name. + // + // +required + Kind string + // Name is the name of resource being referenced. + // It must be a path segment name. + // + // +required + Name string +} + +// PodGroup represents a set of pods with a common scheduling policy. +type PodGroup struct { + // Name is a unique identifier for the PodGroup within the Workload. + // It must be a DNS label. This field is immutable. + // + // +required + Name string + + // Policy defines the scheduling policy for this PodGroup. + // + // +required + Policy PodGroupPolicy +} + +// PodGroupPolicy defines the scheduling configuration for a PodGroup. +type PodGroupPolicy struct { + // Basic specifies that the pods in this group should be scheduled using + // standard Kubernetes scheduling behavior. + // + // +optional + // +oneOf=PolicySelection + Basic *BasicSchedulingPolicy + + // Gang specifies that the pods in this group should be scheduled using + // all-or-nothing semantics. + // + // +optional + // +oneOf=PolicySelection + Gang *GangSchedulingPolicy +} + +// BasicSchedulingPolicy indicates that standard Kubernetes +// scheduling behavior should be used. +type BasicSchedulingPolicy struct { + // This is intentionally empty. Its presence indicates that the basic + // scheduling policy should be applied. In the future, new fields may appear, + // describing such constraints on a pod group level without "all or nothing" + // (gang) scheduling. +} + +// GangSchedulingPolicy defines the parameters for gang scheduling. +type GangSchedulingPolicy struct { + // MinCount is the minimum number of pods that must be schedulable or scheduled + // at the same time for the scheduler to admit the entire group. + // It must be a positive integer. + // + // +required + MinCount int32 +} diff --git a/pkg/apis/scheduling/v1/helpers.go b/pkg/apis/scheduling/v1/helpers.go index d4af4933cfa69..907014a7f4254 100644 --- a/pkg/apis/scheduling/v1/helpers.go +++ b/pkg/apis/scheduling/v1/helpers.go @@ -43,16 +43,29 @@ var systemPriorityClasses = []*v1.PriorityClass{ }, } -// SystemPriorityClasses returns the list of system priority classes. -// NOTE: be careful not to modify any of elements of the returned array directly. +// SystemPriorityClasses returns a deep copy of the list of system priority classes. +// If only the names are needed, use SystemPriorityClassNames(). func SystemPriorityClasses() []*v1.PriorityClass { - return systemPriorityClasses + retval := make([]*v1.PriorityClass, 0, len(systemPriorityClasses)) + for _, c := range systemPriorityClasses { + retval = append(retval, c.DeepCopy()) + } + return retval +} + +// SystemPriorityClassNames returns the names of system priority classes. +func SystemPriorityClassNames() []string { + retval := make([]string, 0, len(systemPriorityClasses)) + for _, c := range systemPriorityClasses { + retval = append(retval, c.Name) + } + return retval } // IsKnownSystemPriorityClass returns true if there's any of the system priority classes exactly // matches "name", "value", "globalDefault". otherwise it will return an error. func IsKnownSystemPriorityClass(name string, value int32, globalDefault bool) (bool, error) { - for _, spc := range SystemPriorityClasses() { + for _, spc := range systemPriorityClasses { if spc.Name == name { if spc.Value != value { return false, fmt.Errorf("value of %v PriorityClass must be %v", spc.Name, spc.Value) diff --git a/pkg/apis/scheduling/v1alpha1/zz_generated.conversion.go b/pkg/apis/scheduling/v1alpha1/zz_generated.conversion.go index fb1c3b4203d6f..994deb7e22add 100644 --- a/pkg/apis/scheduling/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/scheduling/v1alpha1/zz_generated.conversion.go @@ -39,6 +39,46 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.BasicSchedulingPolicy)(nil), (*scheduling.BasicSchedulingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_BasicSchedulingPolicy_To_scheduling_BasicSchedulingPolicy(a.(*schedulingv1alpha1.BasicSchedulingPolicy), b.(*scheduling.BasicSchedulingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.BasicSchedulingPolicy)(nil), (*schedulingv1alpha1.BasicSchedulingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_BasicSchedulingPolicy_To_v1alpha1_BasicSchedulingPolicy(a.(*scheduling.BasicSchedulingPolicy), b.(*schedulingv1alpha1.BasicSchedulingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.GangSchedulingPolicy)(nil), (*scheduling.GangSchedulingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_GangSchedulingPolicy_To_scheduling_GangSchedulingPolicy(a.(*schedulingv1alpha1.GangSchedulingPolicy), b.(*scheduling.GangSchedulingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.GangSchedulingPolicy)(nil), (*schedulingv1alpha1.GangSchedulingPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_GangSchedulingPolicy_To_v1alpha1_GangSchedulingPolicy(a.(*scheduling.GangSchedulingPolicy), b.(*schedulingv1alpha1.GangSchedulingPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.PodGroup)(nil), (*scheduling.PodGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_PodGroup_To_scheduling_PodGroup(a.(*schedulingv1alpha1.PodGroup), b.(*scheduling.PodGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.PodGroup)(nil), (*schedulingv1alpha1.PodGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_PodGroup_To_v1alpha1_PodGroup(a.(*scheduling.PodGroup), b.(*schedulingv1alpha1.PodGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.PodGroupPolicy)(nil), (*scheduling.PodGroupPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy(a.(*schedulingv1alpha1.PodGroupPolicy), b.(*scheduling.PodGroupPolicy), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.PodGroupPolicy)(nil), (*schedulingv1alpha1.PodGroupPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy(a.(*scheduling.PodGroupPolicy), b.(*schedulingv1alpha1.PodGroupPolicy), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.PriorityClass)(nil), (*scheduling.PriorityClass)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_PriorityClass_To_scheduling_PriorityClass(a.(*schedulingv1alpha1.PriorityClass), b.(*scheduling.PriorityClass), scope) }); err != nil { @@ -59,9 +99,135 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.TypedLocalObjectReference)(nil), (*scheduling.TypedLocalObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_TypedLocalObjectReference_To_scheduling_TypedLocalObjectReference(a.(*schedulingv1alpha1.TypedLocalObjectReference), b.(*scheduling.TypedLocalObjectReference), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.TypedLocalObjectReference)(nil), (*schedulingv1alpha1.TypedLocalObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_TypedLocalObjectReference_To_v1alpha1_TypedLocalObjectReference(a.(*scheduling.TypedLocalObjectReference), b.(*schedulingv1alpha1.TypedLocalObjectReference), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.Workload)(nil), (*scheduling.Workload)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_Workload_To_scheduling_Workload(a.(*schedulingv1alpha1.Workload), b.(*scheduling.Workload), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.Workload)(nil), (*schedulingv1alpha1.Workload)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_Workload_To_v1alpha1_Workload(a.(*scheduling.Workload), b.(*schedulingv1alpha1.Workload), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.WorkloadList)(nil), (*scheduling.WorkloadList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_WorkloadList_To_scheduling_WorkloadList(a.(*schedulingv1alpha1.WorkloadList), b.(*scheduling.WorkloadList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.WorkloadList)(nil), (*schedulingv1alpha1.WorkloadList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_WorkloadList_To_v1alpha1_WorkloadList(a.(*scheduling.WorkloadList), b.(*schedulingv1alpha1.WorkloadList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*schedulingv1alpha1.WorkloadSpec)(nil), (*scheduling.WorkloadSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec(a.(*schedulingv1alpha1.WorkloadSpec), b.(*scheduling.WorkloadSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*scheduling.WorkloadSpec)(nil), (*schedulingv1alpha1.WorkloadSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec(a.(*scheduling.WorkloadSpec), b.(*schedulingv1alpha1.WorkloadSpec), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_BasicSchedulingPolicy_To_scheduling_BasicSchedulingPolicy(in *schedulingv1alpha1.BasicSchedulingPolicy, out *scheduling.BasicSchedulingPolicy, s conversion.Scope) error { + return nil +} + +// Convert_v1alpha1_BasicSchedulingPolicy_To_scheduling_BasicSchedulingPolicy is an autogenerated conversion function. +func Convert_v1alpha1_BasicSchedulingPolicy_To_scheduling_BasicSchedulingPolicy(in *schedulingv1alpha1.BasicSchedulingPolicy, out *scheduling.BasicSchedulingPolicy, s conversion.Scope) error { + return autoConvert_v1alpha1_BasicSchedulingPolicy_To_scheduling_BasicSchedulingPolicy(in, out, s) +} + +func autoConvert_scheduling_BasicSchedulingPolicy_To_v1alpha1_BasicSchedulingPolicy(in *scheduling.BasicSchedulingPolicy, out *schedulingv1alpha1.BasicSchedulingPolicy, s conversion.Scope) error { + return nil +} + +// Convert_scheduling_BasicSchedulingPolicy_To_v1alpha1_BasicSchedulingPolicy is an autogenerated conversion function. +func Convert_scheduling_BasicSchedulingPolicy_To_v1alpha1_BasicSchedulingPolicy(in *scheduling.BasicSchedulingPolicy, out *schedulingv1alpha1.BasicSchedulingPolicy, s conversion.Scope) error { + return autoConvert_scheduling_BasicSchedulingPolicy_To_v1alpha1_BasicSchedulingPolicy(in, out, s) +} + +func autoConvert_v1alpha1_GangSchedulingPolicy_To_scheduling_GangSchedulingPolicy(in *schedulingv1alpha1.GangSchedulingPolicy, out *scheduling.GangSchedulingPolicy, s conversion.Scope) error { + out.MinCount = in.MinCount + return nil +} + +// Convert_v1alpha1_GangSchedulingPolicy_To_scheduling_GangSchedulingPolicy is an autogenerated conversion function. +func Convert_v1alpha1_GangSchedulingPolicy_To_scheduling_GangSchedulingPolicy(in *schedulingv1alpha1.GangSchedulingPolicy, out *scheduling.GangSchedulingPolicy, s conversion.Scope) error { + return autoConvert_v1alpha1_GangSchedulingPolicy_To_scheduling_GangSchedulingPolicy(in, out, s) +} + +func autoConvert_scheduling_GangSchedulingPolicy_To_v1alpha1_GangSchedulingPolicy(in *scheduling.GangSchedulingPolicy, out *schedulingv1alpha1.GangSchedulingPolicy, s conversion.Scope) error { + out.MinCount = in.MinCount + return nil +} + +// Convert_scheduling_GangSchedulingPolicy_To_v1alpha1_GangSchedulingPolicy is an autogenerated conversion function. +func Convert_scheduling_GangSchedulingPolicy_To_v1alpha1_GangSchedulingPolicy(in *scheduling.GangSchedulingPolicy, out *schedulingv1alpha1.GangSchedulingPolicy, s conversion.Scope) error { + return autoConvert_scheduling_GangSchedulingPolicy_To_v1alpha1_GangSchedulingPolicy(in, out, s) +} + +func autoConvert_v1alpha1_PodGroup_To_scheduling_PodGroup(in *schedulingv1alpha1.PodGroup, out *scheduling.PodGroup, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy(&in.Policy, &out.Policy, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_PodGroup_To_scheduling_PodGroup is an autogenerated conversion function. +func Convert_v1alpha1_PodGroup_To_scheduling_PodGroup(in *schedulingv1alpha1.PodGroup, out *scheduling.PodGroup, s conversion.Scope) error { + return autoConvert_v1alpha1_PodGroup_To_scheduling_PodGroup(in, out, s) +} + +func autoConvert_scheduling_PodGroup_To_v1alpha1_PodGroup(in *scheduling.PodGroup, out *schedulingv1alpha1.PodGroup, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy(&in.Policy, &out.Policy, s); err != nil { + return err + } return nil } +// Convert_scheduling_PodGroup_To_v1alpha1_PodGroup is an autogenerated conversion function. +func Convert_scheduling_PodGroup_To_v1alpha1_PodGroup(in *scheduling.PodGroup, out *schedulingv1alpha1.PodGroup, s conversion.Scope) error { + return autoConvert_scheduling_PodGroup_To_v1alpha1_PodGroup(in, out, s) +} + +func autoConvert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy(in *schedulingv1alpha1.PodGroupPolicy, out *scheduling.PodGroupPolicy, s conversion.Scope) error { + out.Basic = (*scheduling.BasicSchedulingPolicy)(unsafe.Pointer(in.Basic)) + out.Gang = (*scheduling.GangSchedulingPolicy)(unsafe.Pointer(in.Gang)) + return nil +} + +// Convert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy is an autogenerated conversion function. +func Convert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy(in *schedulingv1alpha1.PodGroupPolicy, out *scheduling.PodGroupPolicy, s conversion.Scope) error { + return autoConvert_v1alpha1_PodGroupPolicy_To_scheduling_PodGroupPolicy(in, out, s) +} + +func autoConvert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy(in *scheduling.PodGroupPolicy, out *schedulingv1alpha1.PodGroupPolicy, s conversion.Scope) error { + out.Basic = (*schedulingv1alpha1.BasicSchedulingPolicy)(unsafe.Pointer(in.Basic)) + out.Gang = (*schedulingv1alpha1.GangSchedulingPolicy)(unsafe.Pointer(in.Gang)) + return nil +} + +// Convert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy is an autogenerated conversion function. +func Convert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy(in *scheduling.PodGroupPolicy, out *schedulingv1alpha1.PodGroupPolicy, s conversion.Scope) error { + return autoConvert_scheduling_PodGroupPolicy_To_v1alpha1_PodGroupPolicy(in, out, s) +} + func autoConvert_v1alpha1_PriorityClass_To_scheduling_PriorityClass(in *schedulingv1alpha1.PriorityClass, out *scheduling.PriorityClass, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta out.Value = in.Value @@ -111,3 +277,97 @@ func autoConvert_scheduling_PriorityClassList_To_v1alpha1_PriorityClassList(in * func Convert_scheduling_PriorityClassList_To_v1alpha1_PriorityClassList(in *scheduling.PriorityClassList, out *schedulingv1alpha1.PriorityClassList, s conversion.Scope) error { return autoConvert_scheduling_PriorityClassList_To_v1alpha1_PriorityClassList(in, out, s) } + +func autoConvert_v1alpha1_TypedLocalObjectReference_To_scheduling_TypedLocalObjectReference(in *schedulingv1alpha1.TypedLocalObjectReference, out *scheduling.TypedLocalObjectReference, s conversion.Scope) error { + out.APIGroup = in.APIGroup + out.Kind = in.Kind + out.Name = in.Name + return nil +} + +// Convert_v1alpha1_TypedLocalObjectReference_To_scheduling_TypedLocalObjectReference is an autogenerated conversion function. +func Convert_v1alpha1_TypedLocalObjectReference_To_scheduling_TypedLocalObjectReference(in *schedulingv1alpha1.TypedLocalObjectReference, out *scheduling.TypedLocalObjectReference, s conversion.Scope) error { + return autoConvert_v1alpha1_TypedLocalObjectReference_To_scheduling_TypedLocalObjectReference(in, out, s) +} + +func autoConvert_scheduling_TypedLocalObjectReference_To_v1alpha1_TypedLocalObjectReference(in *scheduling.TypedLocalObjectReference, out *schedulingv1alpha1.TypedLocalObjectReference, s conversion.Scope) error { + out.APIGroup = in.APIGroup + out.Kind = in.Kind + out.Name = in.Name + return nil +} + +// Convert_scheduling_TypedLocalObjectReference_To_v1alpha1_TypedLocalObjectReference is an autogenerated conversion function. +func Convert_scheduling_TypedLocalObjectReference_To_v1alpha1_TypedLocalObjectReference(in *scheduling.TypedLocalObjectReference, out *schedulingv1alpha1.TypedLocalObjectReference, s conversion.Scope) error { + return autoConvert_scheduling_TypedLocalObjectReference_To_v1alpha1_TypedLocalObjectReference(in, out, s) +} + +func autoConvert_v1alpha1_Workload_To_scheduling_Workload(in *schedulingv1alpha1.Workload, out *scheduling.Workload, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha1_Workload_To_scheduling_Workload is an autogenerated conversion function. +func Convert_v1alpha1_Workload_To_scheduling_Workload(in *schedulingv1alpha1.Workload, out *scheduling.Workload, s conversion.Scope) error { + return autoConvert_v1alpha1_Workload_To_scheduling_Workload(in, out, s) +} + +func autoConvert_scheduling_Workload_To_v1alpha1_Workload(in *scheduling.Workload, out *schedulingv1alpha1.Workload, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +// Convert_scheduling_Workload_To_v1alpha1_Workload is an autogenerated conversion function. +func Convert_scheduling_Workload_To_v1alpha1_Workload(in *scheduling.Workload, out *schedulingv1alpha1.Workload, s conversion.Scope) error { + return autoConvert_scheduling_Workload_To_v1alpha1_Workload(in, out, s) +} + +func autoConvert_v1alpha1_WorkloadList_To_scheduling_WorkloadList(in *schedulingv1alpha1.WorkloadList, out *scheduling.WorkloadList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]scheduling.Workload)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1alpha1_WorkloadList_To_scheduling_WorkloadList is an autogenerated conversion function. +func Convert_v1alpha1_WorkloadList_To_scheduling_WorkloadList(in *schedulingv1alpha1.WorkloadList, out *scheduling.WorkloadList, s conversion.Scope) error { + return autoConvert_v1alpha1_WorkloadList_To_scheduling_WorkloadList(in, out, s) +} + +func autoConvert_scheduling_WorkloadList_To_v1alpha1_WorkloadList(in *scheduling.WorkloadList, out *schedulingv1alpha1.WorkloadList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]schedulingv1alpha1.Workload)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_scheduling_WorkloadList_To_v1alpha1_WorkloadList is an autogenerated conversion function. +func Convert_scheduling_WorkloadList_To_v1alpha1_WorkloadList(in *scheduling.WorkloadList, out *schedulingv1alpha1.WorkloadList, s conversion.Scope) error { + return autoConvert_scheduling_WorkloadList_To_v1alpha1_WorkloadList(in, out, s) +} + +func autoConvert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec(in *schedulingv1alpha1.WorkloadSpec, out *scheduling.WorkloadSpec, s conversion.Scope) error { + out.ControllerRef = (*scheduling.TypedLocalObjectReference)(unsafe.Pointer(in.ControllerRef)) + out.PodGroups = *(*[]scheduling.PodGroup)(unsafe.Pointer(&in.PodGroups)) + return nil +} + +// Convert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec is an autogenerated conversion function. +func Convert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec(in *schedulingv1alpha1.WorkloadSpec, out *scheduling.WorkloadSpec, s conversion.Scope) error { + return autoConvert_v1alpha1_WorkloadSpec_To_scheduling_WorkloadSpec(in, out, s) +} + +func autoConvert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec(in *scheduling.WorkloadSpec, out *schedulingv1alpha1.WorkloadSpec, s conversion.Scope) error { + out.ControllerRef = (*schedulingv1alpha1.TypedLocalObjectReference)(unsafe.Pointer(in.ControllerRef)) + out.PodGroups = *(*[]schedulingv1alpha1.PodGroup)(unsafe.Pointer(&in.PodGroups)) + return nil +} + +// Convert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec is an autogenerated conversion function. +func Convert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec(in *scheduling.WorkloadSpec, out *schedulingv1alpha1.WorkloadSpec, s conversion.Scope) error { + return autoConvert_scheduling_WorkloadSpec_To_v1alpha1_WorkloadSpec(in, out, s) +} diff --git a/pkg/apis/scheduling/validation/validation.go b/pkg/apis/scheduling/validation/validation.go index 2812f9acbfd7a..6ca22364544d3 100644 --- a/pkg/apis/scheduling/validation/validation.go +++ b/pkg/apis/scheduling/validation/validation.go @@ -21,6 +21,9 @@ import ( "strings" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" + pathvalidation "k8s.io/apimachinery/pkg/api/validation/path" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/scheduling" @@ -61,3 +64,120 @@ func ValidatePriorityClassUpdate(pc, oldPc *scheduling.PriorityClass) field.Erro allErrs = append(allErrs, apivalidation.ValidateImmutableField(pc.PreemptionPolicy, oldPc.PreemptionPolicy, field.NewPath("preemptionPolicy"))...) return allErrs } + +// ValidateWorkload tests if all fields in a Workload are set correctly. +func ValidateWorkload(workload *scheduling.Workload) field.ErrorList { + allErrs := apivalidation.ValidateObjectMeta(&workload.ObjectMeta, true, apivalidation.ValidateWorkloadName, field.NewPath("metadata")) + allErrs = append(allErrs, validateWorkloadSpec(&workload.Spec, field.NewPath("spec"))...) + return allErrs +} + +func validateWorkloadSpec(spec *scheduling.WorkloadSpec, fldPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + if spec.ControllerRef != nil { + allErrs = append(allErrs, validateControllerRef(spec.ControllerRef, fldPath.Child("controllerRef"))...) + } + existingPodGroups := sets.New[string]() + podGroupsPath := fldPath.Child("podGroups") + if len(spec.PodGroups) == 0 { + allErrs = append(allErrs, field.Required(podGroupsPath, "must have at least one item")) + } else if len(spec.PodGroups) > scheduling.WorkloadMaxPodGroups { + allErrs = append(allErrs, field.TooMany(fldPath.Child("podGroups"), len(spec.PodGroups), scheduling.WorkloadMaxPodGroups)) + } else { + for i := range spec.PodGroups { + allErrs = append(allErrs, validatePodGroup(&spec.PodGroups[i], podGroupsPath.Index(i), existingPodGroups)...) + } + } + return allErrs +} + +func validateControllerRef(ref *scheduling.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList { + var allErrs = field.ErrorList{} + if ref.APIGroup != "" { + for _, msg := range validation.IsDNS1123Subdomain(ref.APIGroup) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("apiGroup"), ref.APIGroup, msg)) + } + } + if ref.Kind == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("kind"), "")) + } else { + for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Kind) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ref.Kind, msg)) + } + } + if ref.Name == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("name"), "")) + } else { + for _, msg := range pathvalidation.IsValidPathSegmentName(ref.Name) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ref.Name, msg)) + } + } + return allErrs +} + +func validatePodGroup(podGroup *scheduling.PodGroup, fldPath *field.Path, existingPodGroups sets.Set[string]) field.ErrorList { + var allErrs field.ErrorList + for _, detail := range apivalidation.ValidatePodGroupName(podGroup.Name, false) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), podGroup.Name, detail)) + } + if existingPodGroups.Has(podGroup.Name) { + allErrs = append(allErrs, field.Duplicate(fldPath.Child("name"), podGroup.Name)) + } else { + existingPodGroups.Insert(podGroup.Name) + } + allErrs = append(allErrs, validatePodGroupPolicy(&podGroup.Policy, fldPath.Child("policy"))...) + return allErrs +} + +func validatePodGroupPolicy(policy *scheduling.PodGroupPolicy, fldPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + var setFields []string + + if policy.Basic != nil { + setFields = append(setFields, "`basic`") + } + if policy.Gang != nil { + setFields = append(setFields, "`gang`") + } + + switch { + case len(setFields) == 0: + allErrs = append(allErrs, field.Required(fldPath, "exactly one of `basic`, `gang` is required")) + case len(setFields) > 1: + allErrs = append(allErrs, field.Invalid(fldPath, fmt.Sprintf("{%s}", strings.Join(setFields, ", ")), + "exactly one of `basic`, `gang` is required, but multiple fields are set")) + case policy.Basic != nil: + allErrs = append(allErrs, validatBasicSchedulingPolicy(policy.Basic, fldPath.Child("basic"))...) + case policy.Gang != nil: + allErrs = append(allErrs, validateGangSchedulingPolicy(policy.Gang, fldPath.Child("gang"))...) + } + + return allErrs +} + +func validatBasicSchedulingPolicy(policy *scheduling.BasicSchedulingPolicy, fldPath *field.Path) field.ErrorList { + // BasicSchedulingPolicy has no fields. + return nil +} + +func validateGangSchedulingPolicy(policy *scheduling.GangSchedulingPolicy, fldPath *field.Path) field.ErrorList { + allErrs := apivalidation.ValidatePositiveField(int64(policy.MinCount), fldPath.Child("minCount")) + return allErrs +} + +// ValidateWorkloadUpdate tests if an update to Workload is valid. +func ValidateWorkloadUpdate(workload, oldWorkload *scheduling.Workload) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&workload.ObjectMeta, &oldWorkload.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, validateWorkloadSpec(&workload.Spec, field.NewPath("spec"))...) + allErrs = append(allErrs, validateWorkloadSpecUpdate(&workload.Spec, &oldWorkload.Spec, field.NewPath("spec"))...) + return allErrs +} + +func validateWorkloadSpecUpdate(spec, oldSpec *scheduling.WorkloadSpec, fldPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + if oldSpec.ControllerRef != nil { + allErrs = apimachineryvalidation.ValidateImmutableField(spec.ControllerRef, oldSpec.ControllerRef, field.NewPath("controllerRef")) + } + allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.PodGroups, oldSpec.PodGroups, field.NewPath("podGroups"))...) + return allErrs +} diff --git a/pkg/apis/scheduling/validation/validation_test.go b/pkg/apis/scheduling/validation/validation_test.go index 1316b519c2eeb..8372169b5ae15 100644 --- a/pkg/apis/scheduling/validation/validation_test.go +++ b/pkg/apis/scheduling/validation/validation_test.go @@ -17,6 +17,8 @@ limitations under the License. package validation import ( + "fmt" + "strings" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -180,3 +182,230 @@ func TestValidatePriorityClassUpdate(t *testing.T) { } } } + +func TestValidateWorkload(t *testing.T) { + successCases := map[string]*scheduling.Workload{ + "basic and gang policies": mkWorkload(), + "no controllerRef": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef = nil + }), + "no controllerRef apiGroup": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.APIGroup = "" + }), + } + for name, workload := range successCases { + errs := ValidateWorkload(workload) + if len(errs) != 0 { + t.Errorf("Expected success for %q: %v", name, errs) + } + } + + failureCases := map[string]*scheduling.Workload{ + "no name": mkWorkload(func(w *scheduling.Workload) { + w.Name = "" + }), + "invalid name": mkWorkload(func(w *scheduling.Workload) { + w.Name = ".workload" + }), + "too long name": mkWorkload(func(w *scheduling.Workload) { + w.Name = strings.Repeat("w", 254) + }), + "no namespace": mkWorkload(func(w *scheduling.Workload) { + w.Namespace = "" + }), + "invalid namespace": mkWorkload(func(w *scheduling.Workload) { + w.Namespace = ".ns" + }), + "too long namespace": mkWorkload(func(w *scheduling.Workload) { + w.Namespace = strings.Repeat("n", 64) + }), + "invalid controllerRef apiGroup": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.APIGroup = ".group" + }), + "too long controllerRef apiGroup": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.APIGroup = strings.Repeat("g", 254) + }), + "no controllerRef kind": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.Kind = "" + }), + "invalid controllerRef kind": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.Kind = "/foo" + }), + "no controllerRef name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.Name = "" + }), + "invalid controllerRef name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.Name = "/baz" + }), + "no pod groups": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups = nil + }), + "too many pod groups": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups = nil + for i := 0; i < scheduling.WorkloadMaxPodGroups+1; i++ { + w.Spec.PodGroups = append(w.Spec.PodGroups, scheduling.PodGroup{ + Name: fmt.Sprintf("group-%v", i), + Policy: scheduling.PodGroupPolicy{ + Basic: &scheduling.BasicSchedulingPolicy{}, + }, + }) + } + }), + "no pod group name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Name = "" + }), + "invalid pod group name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Name = ".group1" + }), + "too long pod group name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Name = strings.Repeat("g", 64) + }), + "no policy set": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Policy = scheduling.PodGroupPolicy{} + }), + "two policies": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Policy.Gang = &scheduling.GangSchedulingPolicy{ + MinCount: 2, + } + }), + "zero min count in gang": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[1].Policy.Gang.MinCount = 0 + }), + "negative min count in gang": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[1].Policy.Gang.MinCount = -1 + }), + "two pod groups with the same name": mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[1].Name = w.Spec.PodGroups[0].Name + }), + } + for name, workload := range failureCases { + errs := ValidateWorkload(workload) + if len(errs) == 0 { + t.Errorf("Expected failure for %q", name) + } + } +} + +func TestValidateWorkloadUpdate(t *testing.T) { + successCases := map[string]struct { + old *scheduling.Workload + update *scheduling.Workload + }{ + "no change": { + old: mkWorkload(), + update: mkWorkload(), + }, + "set controller ref": { + old: mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef = nil + }), + update: mkWorkload(), + }, + } + for name, tc := range successCases { + tc.old.ResourceVersion = "0" + tc.update.ResourceVersion = "1" + errs := ValidateWorkloadUpdate(tc.update, tc.old) + if len(errs) != 0 { + t.Errorf("Expected success for %q: %v", name, errs) + } + } + + failureCases := map[string]struct { + old *scheduling.Workload + update *scheduling.Workload + }{ + "change name": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Name += "bar" + }), + }, + "change namespace": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Namespace += "bar" + }), + }, + "change pod group name": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[0].Name += "bar" + }), + }, + "add pod group": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups = append(w.Spec.PodGroups, scheduling.PodGroup{ + Name: "group3", + Policy: scheduling.PodGroupPolicy{ + Basic: &scheduling.BasicSchedulingPolicy{}, + }, + }) + }), + }, + "delete pod group": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups = w.Spec.PodGroups[:1] + }), + }, + "change gang min count": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.PodGroups[1].Policy.Gang.MinCount = 5 + }), + }, + "change controllerRef": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef.Kind += "bar" + }), + }, + "delete controllerRef": { + old: mkWorkload(), + update: mkWorkload(func(w *scheduling.Workload) { + w.Spec.ControllerRef = nil + }), + }, + } + for name, tc := range failureCases { + tc.old.ResourceVersion = "0" + tc.update.ResourceVersion = "1" + errs := ValidateWorkloadUpdate(tc.update, tc.old) + if len(errs) == 0 { + t.Errorf("Expected failure for %q", name) + } + } +} + +// mkWorkload produces a Workload which passes validation with no tweaks. +func mkWorkload(tweaks ...func(w *scheduling.Workload)) *scheduling.Workload { + w := &scheduling.Workload{ + ObjectMeta: metav1.ObjectMeta{Name: "workload", Namespace: "ns"}, + Spec: scheduling.WorkloadSpec{ + ControllerRef: &scheduling.TypedLocalObjectReference{ + APIGroup: "group", + Kind: "foo", + Name: "baz", + }, + PodGroups: []scheduling.PodGroup{{ + Name: "group1", + Policy: scheduling.PodGroupPolicy{ + Basic: &scheduling.BasicSchedulingPolicy{}, + }, + }, { + Name: "group2", + Policy: scheduling.PodGroupPolicy{ + Gang: &scheduling.GangSchedulingPolicy{ + MinCount: 2, + }, + }, + }}, + }, + } + for _, tweak := range tweaks { + tweak(w) + } + return w +} diff --git a/pkg/apis/scheduling/zz_generated.deepcopy.go b/pkg/apis/scheduling/zz_generated.deepcopy.go index 04ab256185d82..853afd8c1c468 100644 --- a/pkg/apis/scheduling/zz_generated.deepcopy.go +++ b/pkg/apis/scheduling/zz_generated.deepcopy.go @@ -26,6 +26,81 @@ import ( core "k8s.io/kubernetes/pkg/apis/core" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicSchedulingPolicy) DeepCopyInto(out *BasicSchedulingPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicSchedulingPolicy. +func (in *BasicSchedulingPolicy) DeepCopy() *BasicSchedulingPolicy { + if in == nil { + return nil + } + out := new(BasicSchedulingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GangSchedulingPolicy) DeepCopyInto(out *GangSchedulingPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GangSchedulingPolicy. +func (in *GangSchedulingPolicy) DeepCopy() *GangSchedulingPolicy { + if in == nil { + return nil + } + out := new(GangSchedulingPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroup) DeepCopyInto(out *PodGroup) { + *out = *in + in.Policy.DeepCopyInto(&out.Policy) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroup. +func (in *PodGroup) DeepCopy() *PodGroup { + if in == nil { + return nil + } + out := new(PodGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodGroupPolicy) DeepCopyInto(out *PodGroupPolicy) { + *out = *in + if in.Basic != nil { + in, out := &in.Basic, &out.Basic + *out = new(BasicSchedulingPolicy) + **out = **in + } + if in.Gang != nil { + in, out := &in.Gang, &out.Gang + *out = new(GangSchedulingPolicy) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodGroupPolicy. +func (in *PodGroupPolicy) DeepCopy() *PodGroupPolicy { + if in == nil { + return nil + } + out := new(PodGroupPolicy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PriorityClass) DeepCopyInto(out *PriorityClass) { *out = *in @@ -89,3 +164,107 @@ func (in *PriorityClassList) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TypedLocalObjectReference) DeepCopyInto(out *TypedLocalObjectReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypedLocalObjectReference. +func (in *TypedLocalObjectReference) DeepCopy() *TypedLocalObjectReference { + if in == nil { + return nil + } + out := new(TypedLocalObjectReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Workload) DeepCopyInto(out *Workload) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workload. +func (in *Workload) DeepCopy() *Workload { + if in == nil { + return nil + } + out := new(Workload) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Workload) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadList) DeepCopyInto(out *WorkloadList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Workload, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadList. +func (in *WorkloadList) DeepCopy() *WorkloadList { + if in == nil { + return nil + } + out := new(WorkloadList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkloadList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadSpec) DeepCopyInto(out *WorkloadSpec) { + *out = *in + if in.ControllerRef != nil { + in, out := &in.ControllerRef, &out.ControllerRef + *out = new(TypedLocalObjectReference) + **out = **in + } + if in.PodGroups != nil { + in, out := &in.PodGroups, &out.PodGroups + *out = make([]PodGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadSpec. +func (in *WorkloadSpec) DeepCopy() *WorkloadSpec { + if in == nil { + return nil + } + out := new(WorkloadSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/storage/types.go b/pkg/apis/storage/types.go index 9590c64c22a56..02aa225f34161 100644 --- a/pkg/apis/storage/types.go +++ b/pkg/apis/storage/types.go @@ -433,6 +433,31 @@ type CSIDriverSpec struct { // +featureGate=MutableCSINodeAllocatableCount // +optional NodeAllocatableUpdatePeriodSeconds *int64 + + // serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that + // service account tokens should be passed via the Secrets field in NodePublishVolumeRequest + // instead of the VolumeContext field. The CSI specification provides a dedicated Secrets + // field for sensitive information like tokens, which is the appropriate mechanism for + // handling credentials. This addresses security concerns where sensitive tokens were being + // logged as part of volume context, leading to vulnerabilities like CVE-2023-2878 and + // CVE-2024-3744. + // + // When "true", kubelet will pass the tokens only in the Secrets field with the key + // "csi.storage.k8s.io/serviceAccount.tokens". The CSI driver must be updated to read + // tokens from the Secrets field instead of VolumeContext. + // + // When "false" or not set, kubelet will pass the tokens in VolumeContext with the key + // "csi.storage.k8s.io/serviceAccount.tokens" (existing behavior). This maintains backward + // compatibility with existing CSI drivers. + // + // This field can only be set when TokenRequests is configured. The API server will reject + // CSIDriver specs that set this field without TokenRequests. + // + // Default is "false". + // + // +featureGate=CSIServiceAccountTokenSecrets + // +optional + ServiceAccountTokenInSecrets *bool } // FSGroupPolicy specifies if a CSI Driver supports modifying diff --git a/pkg/apis/storage/v1/doc.go b/pkg/apis/storage/v1/doc.go index 455fd010a1f6d..7c35db3b1f2e9 100644 --- a/pkg/apis/storage/v1/doc.go +++ b/pkg/apis/storage/v1/doc.go @@ -19,5 +19,7 @@ limitations under the License. // +groupName=storage.k8s.io // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/storage/v1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/storage/v1 package v1 diff --git a/pkg/apis/storage/v1/zz_generated.conversion.go b/pkg/apis/storage/v1/zz_generated.conversion.go index 2183e32ae7081..98b90848128c3 100644 --- a/pkg/apis/storage/v1/zz_generated.conversion.go +++ b/pkg/apis/storage/v1/zz_generated.conversion.go @@ -333,6 +333,7 @@ func autoConvert_v1_CSIDriverSpec_To_storage_CSIDriverSpec(in *storagev1.CSIDriv out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish)) out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount)) out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds)) + out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets)) return nil } @@ -351,6 +352,7 @@ func autoConvert_storage_CSIDriverSpec_To_v1_CSIDriverSpec(in *storage.CSIDriver out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish)) out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount)) out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds)) + out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets)) return nil } diff --git a/pkg/apis/storage/v1/zz_generated.validations.go b/pkg/apis/storage/v1/zz_generated.validations.go new file mode 100644 index 0000000000000..7122d64164dc8 --- /dev/null +++ b/pkg/apis/storage/v1/zz_generated.validations.go @@ -0,0 +1,114 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1 + +import ( + context "context" + fmt "fmt" + + storagev1 "k8s.io/api/storage/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type StorageClass + scheme.AddValidationFunc((*storagev1.StorageClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StorageClass(ctx, op, nil /* fldPath */, obj.(*storagev1.StorageClass), safe.Cast[*storagev1.StorageClass](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type StorageClassList + scheme.AddValidationFunc((*storagev1.StorageClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StorageClassList(ctx, op, nil /* fldPath */, obj.(*storagev1.StorageClassList), safe.Cast[*storagev1.StorageClassList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_StorageClass validates an instance of StorageClass according +// to declarative validation rules in the API schema. +func Validate_StorageClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *storagev1.StorageClass) (errs field.ErrorList) { + // field storagev1.StorageClass.TypeMeta has no validation + // field storagev1.StorageClass.ObjectMeta has no validation + + // field storagev1.StorageClass.Provisioner + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("provisioner"), &obj.Provisioner, safe.Field(oldObj, func(oldObj *storagev1.StorageClass) *string { return &oldObj.Provisioner }), oldObj != nil)...) + + // field storagev1.StorageClass.Parameters has no validation + // field storagev1.StorageClass.ReclaimPolicy has no validation + // field storagev1.StorageClass.MountOptions has no validation + // field storagev1.StorageClass.AllowVolumeExpansion has no validation + // field storagev1.StorageClass.VolumeBindingMode has no validation + // field storagev1.StorageClass.AllowedTopologies has no validation + return errs +} + +// Validate_StorageClassList validates an instance of StorageClassList according +// to declarative validation rules in the API schema. +func Validate_StorageClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *storagev1.StorageClassList) (errs field.ErrorList) { + // field storagev1.StorageClassList.TypeMeta has no validation + // field storagev1.StorageClassList.ListMeta has no validation + + // field storagev1.StorageClassList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []storagev1.StorageClass, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_StorageClass)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *storagev1.StorageClassList) []storagev1.StorageClass { return oldObj.Items }), oldObj != nil)...) + + return errs +} diff --git a/pkg/apis/storage/v1alpha1/doc.go b/pkg/apis/storage/v1alpha1/doc.go index e0e102acc6c63..6bbf17394571b 100644 --- a/pkg/apis/storage/v1alpha1/doc.go +++ b/pkg/apis/storage/v1alpha1/doc.go @@ -19,5 +19,7 @@ limitations under the License. // +groupName=storage.k8s.io // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/storage/v1alpha1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/storage/v1alpha1 package v1alpha1 diff --git a/pkg/apis/storage/v1alpha1/zz_generated.validations.go b/pkg/apis/storage/v1alpha1/zz_generated.validations.go new file mode 100644 index 0000000000000..483365f8c94a1 --- /dev/null +++ b/pkg/apis/storage/v1alpha1/zz_generated.validations.go @@ -0,0 +1,34 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + return nil +} diff --git a/pkg/apis/storage/v1beta1/doc.go b/pkg/apis/storage/v1beta1/doc.go index 60f550386b24a..ee186a821dc49 100644 --- a/pkg/apis/storage/v1beta1/doc.go +++ b/pkg/apis/storage/v1beta1/doc.go @@ -19,5 +19,7 @@ limitations under the License. // +groupName=storage.k8s.io // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/storage/v1beta1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/storage/v1beta1 package v1beta1 diff --git a/pkg/apis/storage/v1beta1/zz_generated.conversion.go b/pkg/apis/storage/v1beta1/zz_generated.conversion.go index c6c30e3634cf5..e72efb43aaa0c 100644 --- a/pkg/apis/storage/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/storage/v1beta1/zz_generated.conversion.go @@ -333,6 +333,7 @@ func autoConvert_v1beta1_CSIDriverSpec_To_storage_CSIDriverSpec(in *storagev1bet out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish)) out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount)) out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds)) + out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets)) return nil } @@ -351,6 +352,7 @@ func autoConvert_storage_CSIDriverSpec_To_v1beta1_CSIDriverSpec(in *storage.CSID out.RequiresRepublish = (*bool)(unsafe.Pointer(in.RequiresRepublish)) out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount)) out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds)) + out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets)) return nil } diff --git a/pkg/apis/storage/v1beta1/zz_generated.validations.go b/pkg/apis/storage/v1beta1/zz_generated.validations.go new file mode 100644 index 0000000000000..fcb28065d2189 --- /dev/null +++ b/pkg/apis/storage/v1beta1/zz_generated.validations.go @@ -0,0 +1,114 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package v1beta1 + +import ( + context "context" + fmt "fmt" + + storagev1beta1 "k8s.io/api/storage/v1beta1" + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + runtime "k8s.io/apimachinery/pkg/runtime" + field "k8s.io/apimachinery/pkg/util/validation/field" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *runtime.Scheme) error { + // type StorageClass + scheme.AddValidationFunc((*storagev1beta1.StorageClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StorageClass(ctx, op, nil /* fldPath */, obj.(*storagev1beta1.StorageClass), safe.Cast[*storagev1beta1.StorageClass](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + // type StorageClassList + scheme.AddValidationFunc((*storagev1beta1.StorageClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StorageClassList(ctx, op, nil /* fldPath */, obj.(*storagev1beta1.StorageClassList), safe.Cast[*storagev1beta1.StorageClassList](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_StorageClass validates an instance of StorageClass according +// to declarative validation rules in the API schema. +func Validate_StorageClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *storagev1beta1.StorageClass) (errs field.ErrorList) { + // field storagev1beta1.StorageClass.TypeMeta has no validation + // field storagev1beta1.StorageClass.ObjectMeta has no validation + + // field storagev1beta1.StorageClass.Provisioner + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + }(fldPath.Child("provisioner"), &obj.Provisioner, safe.Field(oldObj, func(oldObj *storagev1beta1.StorageClass) *string { return &oldObj.Provisioner }), oldObj != nil)...) + + // field storagev1beta1.StorageClass.Parameters has no validation + // field storagev1beta1.StorageClass.ReclaimPolicy has no validation + // field storagev1beta1.StorageClass.MountOptions has no validation + // field storagev1beta1.StorageClass.AllowVolumeExpansion has no validation + // field storagev1beta1.StorageClass.VolumeBindingMode has no validation + // field storagev1beta1.StorageClass.AllowedTopologies has no validation + return errs +} + +// Validate_StorageClassList validates an instance of StorageClassList according +// to declarative validation rules in the API schema. +func Validate_StorageClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *storagev1beta1.StorageClassList) (errs field.ErrorList) { + // field storagev1beta1.StorageClassList.TypeMeta has no validation + // field storagev1beta1.StorageClassList.ListMeta has no validation + + // field storagev1beta1.StorageClassList.Items + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []storagev1beta1.StorageClass, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil + } + // iterate the list and call the type's validation function + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_StorageClass)...) + return + }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *storagev1beta1.StorageClassList) []storagev1beta1.StorageClass { return oldObj.Items }), oldObj != nil)...) + + return errs +} diff --git a/pkg/apis/storage/validation/validation.go b/pkg/apis/storage/validation/validation.go index 6378066bae590..e103b613aae58 100644 --- a/pkg/apis/storage/validation/validation.go +++ b/pkg/apis/storage/validation/validation.go @@ -88,7 +88,7 @@ func ValidateStorageClassUpdate(storageClass, oldStorageClass *storage.StorageCl func validateProvisioner(provisioner string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if len(provisioner) == 0 { - allErrs = append(allErrs, field.Required(fldPath, provisioner)) + allErrs = append(allErrs, field.Required(fldPath, provisioner)).MarkCoveredByDeclarative() } if len(provisioner) > 0 { allErrs = append(allErrs, apivalidation.ValidateQualifiedName(strings.ToLower(provisioner), fldPath)...) @@ -456,6 +456,7 @@ func validateCSIDriverSpec( allErrs = append(allErrs, validateVolumeLifecycleModes(spec.VolumeLifecycleModes, fldPath.Child("volumeLifecycleModes"))...) allErrs = append(allErrs, validateSELinuxMount(spec.SELinuxMount, fldPath.Child("seLinuxMount"))...) allErrs = append(allErrs, validateNodeAllocatableUpdatePeriodSeconds(spec.NodeAllocatableUpdatePeriodSeconds, fldPath.Child("nodeAllocatableUpdatePeriodSeconds"))...) + allErrs = append(allErrs, validateServiceAccountTokenInSecrets(spec.ServiceAccountTokenInSecrets, spec.TokenRequests, fldPath.Child("serviceAccountTokenInSecrets"))...) return allErrs } @@ -572,6 +573,16 @@ func validateSELinuxMount(seLinuxMount *bool, fldPath *field.Path) field.ErrorLi return allErrs } +// validvalidateServiceAccountTokenInSecrets validates serviceAccountTokenInSecrets and its relation to tokenRequests. +func validateServiceAccountTokenInSecrets(serviceAccountTokenInSecrets *bool, tokenRequests []storage.TokenRequest, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if serviceAccountTokenInSecrets != nil && len(tokenRequests) == 0 { + allErrs = append(allErrs, field.Invalid(fldPath, serviceAccountTokenInSecrets, "serviceAccountTokenInSecrets is set but no tokenRequests are specified")) + } + + return allErrs +} + // ValidateStorageCapacityName checks that a name is appropriate for a // CSIStorageCapacity object. var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain diff --git a/pkg/apis/storage/validation/validation_test.go b/pkg/apis/storage/validation/validation_test.go index 0c4cd15de45f5..61b0c99d7de60 100644 --- a/pkg/apis/storage/validation/validation_test.go +++ b/pkg/apis/storage/validation/validation_test.go @@ -1523,6 +1523,7 @@ func TestCSIDriverValidation(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true) // assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountTokenSecrets, true) driverName := "test-driver" longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver" @@ -1536,10 +1537,13 @@ func TestCSIDriverValidation(t *testing.T) { notStorageCapacity := false seLinuxMount := true notSELinuxMount := false + serviceAccountTokenInSecrets := true + notServiceAccountTokenInSecrets := false supportedFSGroupPolicy := storage.FileFSGroupPolicy invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode") validNodeAllocatableUpdatePeriodSeconds := int64(10) invalidNodeAllocatableUpdatePeriodSeconds := int64(9) + tokenRequests := []storage.TokenRequest{{Audience: "test-audience"}} successCases := []storage.CSIDriver{{ ObjectMeta: metav1.ObjectMeta{Name: driverName}, Spec: storage.CSIDriverSpec{ @@ -1709,6 +1713,41 @@ func TestCSIDriverValidation(t *testing.T) { SELinuxMount: &seLinuxMount, NodeAllocatableUpdatePeriodSeconds: &validNodeAllocatableUpdatePeriodSeconds, }, + }, { + // With ServiceAccountTokenInSecrets set to true with TokenRequests + ObjectMeta: metav1.ObjectMeta{Name: driverName}, + Spec: storage.CSIDriverSpec{ + AttachRequired: &attachNotRequired, + PodInfoOnMount: ¬PodInfoOnMount, + RequiresRepublish: ¬RequiresRepublish, + StorageCapacity: &storageCapacity, + SELinuxMount: &seLinuxMount, + ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets, + TokenRequests: tokenRequests, + }, + }, { + // With ServiceAccountTokenInSecrets set to false with TokenRequests + ObjectMeta: metav1.ObjectMeta{Name: driverName}, + Spec: storage.CSIDriverSpec{ + AttachRequired: &attachNotRequired, + PodInfoOnMount: ¬PodInfoOnMount, + RequiresRepublish: ¬RequiresRepublish, + StorageCapacity: &storageCapacity, + SELinuxMount: &seLinuxMount, + ServiceAccountTokenInSecrets: ¬ServiceAccountTokenInSecrets, + TokenRequests: tokenRequests, + }, + }, { + // With ServiceAccountTokenInSecrets set to nil (not set) + ObjectMeta: metav1.ObjectMeta{Name: driverName}, + Spec: storage.CSIDriverSpec{ + AttachRequired: &attachNotRequired, + PodInfoOnMount: ¬PodInfoOnMount, + RequiresRepublish: ¬RequiresRepublish, + StorageCapacity: &storageCapacity, + SELinuxMount: &seLinuxMount, + ServiceAccountTokenInSecrets: nil, + }, }} for _, csiDriver := range successCases { @@ -1799,6 +1838,16 @@ func TestCSIDriverValidation(t *testing.T) { SELinuxMount: &seLinuxMount, NodeAllocatableUpdatePeriodSeconds: &invalidNodeAllocatableUpdatePeriodSeconds, }, + }, { + // ServiceAccountTokenInSecrets set without TokenRequests (invalid) + ObjectMeta: metav1.ObjectMeta{Name: driverName}, + Spec: storage.CSIDriverSpec{ + AttachRequired: &attachNotRequired, + PodInfoOnMount: ¬PodInfoOnMount, + StorageCapacity: &storageCapacity, + SELinuxMount: &seLinuxMount, + ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets, + }, }} for _, csiDriver := range errorCases { @@ -1813,6 +1862,7 @@ func TestCSIDriverValidationUpdate(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true) // assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIServiceAccountTokenSecrets, true) driverName := "test-driver" longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver" @@ -1828,8 +1878,11 @@ func TestCSIDriverValidationUpdate(t *testing.T) { notStorageCapacity := false seLinuxMount := true notSELinuxMount := false + serviceAccountTokenInSecrets := true + notServiceAccountTokenInSecrets := false validNodeAllocatableUpdatePeriodSeconds := int64(10) invalidNodeAllocatableUpdatePeriodSeconds := int64(9) + tokenRequests := []storage.TokenRequest{{Audience: "test-audience"}} old := storage.CSIDriver{ ObjectMeta: metav1.ObjectMeta{Name: driverName, ResourceVersion: "1"}, @@ -1888,6 +1941,26 @@ func TestCSIDriverValidationUpdate(t *testing.T) { modify: func(new *storage.CSIDriver) { new.Spec.NodeAllocatableUpdatePeriodSeconds = &validNodeAllocatableUpdatePeriodSeconds }, + }, { + name: "change ServiceAccountTokenInSecrets from nil to true with TokenRequests", + modify: func(new *storage.CSIDriver) { + new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets + new.Spec.TokenRequests = tokenRequests + }, + }, { + name: "change ServiceAccountTokenInSecrets from nil to false with TokenRequests", + modify: func(new *storage.CSIDriver) { + new.Spec.ServiceAccountTokenInSecrets = ¬ServiceAccountTokenInSecrets + new.Spec.TokenRequests = tokenRequests + }, + }, { + name: "change ServiceAccountTokenInSecrets from true to false", + modify: func(new *storage.CSIDriver) { + new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets + new.Spec.TokenRequests = tokenRequests + old := new.DeepCopy() + old.Spec.ServiceAccountTokenInSecrets = ¬ServiceAccountTokenInSecrets + }, }} for _, test := range successCases { @@ -1980,6 +2053,11 @@ func TestCSIDriverValidationUpdate(t *testing.T) { modify: func(new *storage.CSIDriver) { new.Spec.NodeAllocatableUpdatePeriodSeconds = &invalidNodeAllocatableUpdatePeriodSeconds }, + }, { + name: "ServiceAccountTokenInSecrets set without TokenRequests", + modify: func(new *storage.CSIDriver) { + new.Spec.ServiceAccountTokenInSecrets = &serviceAccountTokenInSecrets + }, }} for _, test := range errorCases { diff --git a/pkg/apis/storage/zz_generated.deepcopy.go b/pkg/apis/storage/zz_generated.deepcopy.go index 331352db3b108..f76e0695da70b 100644 --- a/pkg/apis/storage/zz_generated.deepcopy.go +++ b/pkg/apis/storage/zz_generated.deepcopy.go @@ -137,6 +137,11 @@ func (in *CSIDriverSpec) DeepCopyInto(out *CSIDriverSpec) { *out = new(int64) **out = **in } + if in.ServiceAccountTokenInSecrets != nil { + in, out := &in.ServiceAccountTokenInSecrets, &out.ServiceAccountTokenInSecrets + *out = new(bool) + **out = **in + } return } diff --git a/pkg/apis/storagemigration/install/install.go b/pkg/apis/storagemigration/install/install.go index f5d972a817b59..da100b960d2ac 100644 --- a/pkg/apis/storagemigration/install/install.go +++ b/pkg/apis/storagemigration/install/install.go @@ -20,7 +20,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/storagemigration" - "k8s.io/kubernetes/pkg/apis/storagemigration/v1alpha1" + "k8s.io/kubernetes/pkg/apis/storagemigration/v1beta1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" ) @@ -32,6 +32,6 @@ func init() { // Install registers the API group and adds types to a scheme func Install(scheme *runtime.Scheme) { utilruntime.Must(storagemigration.AddToScheme(scheme)) - utilruntime.Must(v1alpha1.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion)) + utilruntime.Must(v1beta1.AddToScheme(scheme)) + utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion)) } diff --git a/pkg/apis/storagemigration/types.go b/pkg/apis/storagemigration/types.go index bc7ecb9e9dace..5944b67657d5e 100644 --- a/pkg/apis/storagemigration/types.go +++ b/pkg/apis/storagemigration/types.go @@ -17,7 +17,6 @@ limitations under the License. package storagemigration import ( - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -47,25 +46,7 @@ type StorageVersionMigrationSpec struct { // The resource that is being migrated. The migrator sends requests to // the endpoint serving the resource. // Immutable. - Resource GroupVersionResource - // The token used in the list options to get the next chunk of objects - // to migrate. When the .status.conditions indicates the migration is - // "Running", users can use this token to check the progress of the - // migration. - // +optional - ContinueToken string - // TODO: consider recording the storage version hash when the migration - // is created. It can avoid races. -} - -// The names of the group, the version, and the resource. -type GroupVersionResource struct { - // The name of the group. - Group string - // The name of the version. - Version string - // The name of the resource. - Resource string + Resource metav1.GroupResource } type MigrationConditionType string @@ -79,23 +60,6 @@ const ( MigrationFailed MigrationConditionType = "Failed" ) -// Describes the state of a migration at a certain point. -type MigrationCondition struct { - // Type of the condition. - Type MigrationConditionType - // Status of the condition, one of True, False, Unknown. - Status corev1.ConditionStatus - // The last time this condition was updated. - // +optional - LastUpdateTime metav1.Time - // The reason for the condition's last transition. - // +optional - Reason string - // A human readable message indicating details about the transition. - // +optional - Message string -} - // Status of the storage version migration. type StorageVersionMigrationStatus struct { // The latest available observations of the migration's current state. @@ -104,7 +68,7 @@ type StorageVersionMigrationStatus struct { // +listType=map // +listMapKey=type // +optional - Conditions []MigrationCondition + Conditions []metav1.Condition // ResourceVersion to compare with the GC cache for performing the migration. // This is the current resource version of given group, version and resource when // kube-controller-manager first observes this StorageVersionMigration resource. @@ -123,9 +87,5 @@ type StorageVersionMigrationList struct { // +optional metav1.ListMeta // Items is the list of StorageVersionMigration - // +patchMergeKey=type - // +patchStrategy=merge - // +listType=map - // +listMapKey=type Items []StorageVersionMigration } diff --git a/pkg/apis/storagemigration/v1alpha1/doc.go b/pkg/apis/storagemigration/v1alpha1/doc.go deleted file mode 100644 index 67db57a3b543b..0000000000000 --- a/pkg/apis/storagemigration/v1alpha1/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -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. -*/ - -// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/storagemigration -// +k8s:conversion-gen-external-types=k8s.io/api/storagemigration/v1alpha1 -// +groupName=storagemigration.k8s.io -// +k8s:defaulter-gen=TypeMeta -// +k8s:defaulter-gen-input=k8s.io/api/storagemigration/v1alpha1 - -package v1alpha1 diff --git a/pkg/apis/storagemigration/v1alpha1/register.go b/pkg/apis/storagemigration/v1alpha1/register.go deleted file mode 100644 index 4eb8af72ce2ed..0000000000000 --- a/pkg/apis/storagemigration/v1alpha1/register.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -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 v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - - svmv1alpha1 "k8s.io/api/storagemigration/v1alpha1" -) - -// GroupName is the group name use in this package -const GroupName = "storagemigration.k8s.io" - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - localSchemeBuilder = &svmv1alpha1.SchemeBuilder - AddToScheme = localSchemeBuilder.AddToScheme -) diff --git a/pkg/apis/storagemigration/v1alpha1/zz_generated.conversion.go b/pkg/apis/storagemigration/v1alpha1/zz_generated.conversion.go deleted file mode 100644 index 2830a7fe1fea6..0000000000000 --- a/pkg/apis/storagemigration/v1alpha1/zz_generated.conversion.go +++ /dev/null @@ -1,256 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by conversion-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - unsafe "unsafe" - - v1 "k8s.io/api/core/v1" - storagemigrationv1alpha1 "k8s.io/api/storagemigration/v1alpha1" - conversion "k8s.io/apimachinery/pkg/conversion" - runtime "k8s.io/apimachinery/pkg/runtime" - storagemigration "k8s.io/kubernetes/pkg/apis/storagemigration" -) - -func init() { - localSchemeBuilder.Register(RegisterConversions) -} - -// RegisterConversions adds conversion functions to the given scheme. -// Public to allow building arbitrary schemes. -func RegisterConversions(s *runtime.Scheme) error { - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.GroupVersionResource)(nil), (*storagemigration.GroupVersionResource)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource(a.(*storagemigrationv1alpha1.GroupVersionResource), b.(*storagemigration.GroupVersionResource), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.GroupVersionResource)(nil), (*storagemigrationv1alpha1.GroupVersionResource)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource(a.(*storagemigration.GroupVersionResource), b.(*storagemigrationv1alpha1.GroupVersionResource), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.MigrationCondition)(nil), (*storagemigration.MigrationCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_MigrationCondition_To_storagemigration_MigrationCondition(a.(*storagemigrationv1alpha1.MigrationCondition), b.(*storagemigration.MigrationCondition), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.MigrationCondition)(nil), (*storagemigrationv1alpha1.MigrationCondition)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_MigrationCondition_To_v1alpha1_MigrationCondition(a.(*storagemigration.MigrationCondition), b.(*storagemigrationv1alpha1.MigrationCondition), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.StorageVersionMigration)(nil), (*storagemigration.StorageVersionMigration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(a.(*storagemigrationv1alpha1.StorageVersionMigration), b.(*storagemigration.StorageVersionMigration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigration)(nil), (*storagemigrationv1alpha1.StorageVersionMigration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_StorageVersionMigration_To_v1alpha1_StorageVersionMigration(a.(*storagemigration.StorageVersionMigration), b.(*storagemigrationv1alpha1.StorageVersionMigration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.StorageVersionMigrationList)(nil), (*storagemigration.StorageVersionMigrationList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(a.(*storagemigrationv1alpha1.StorageVersionMigrationList), b.(*storagemigration.StorageVersionMigrationList), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationList)(nil), (*storagemigrationv1alpha1.StorageVersionMigrationList)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_StorageVersionMigrationList_To_v1alpha1_StorageVersionMigrationList(a.(*storagemigration.StorageVersionMigrationList), b.(*storagemigrationv1alpha1.StorageVersionMigrationList), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.StorageVersionMigrationSpec)(nil), (*storagemigration.StorageVersionMigrationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(a.(*storagemigrationv1alpha1.StorageVersionMigrationSpec), b.(*storagemigration.StorageVersionMigrationSpec), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationSpec)(nil), (*storagemigrationv1alpha1.StorageVersionMigrationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec(a.(*storagemigration.StorageVersionMigrationSpec), b.(*storagemigrationv1alpha1.StorageVersionMigrationSpec), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigrationv1alpha1.StorageVersionMigrationStatus)(nil), (*storagemigration.StorageVersionMigrationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(a.(*storagemigrationv1alpha1.StorageVersionMigrationStatus), b.(*storagemigration.StorageVersionMigrationStatus), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationStatus)(nil), (*storagemigrationv1alpha1.StorageVersionMigrationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus(a.(*storagemigration.StorageVersionMigrationStatus), b.(*storagemigrationv1alpha1.StorageVersionMigrationStatus), scope) - }); err != nil { - return err - } - return nil -} - -func autoConvert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource(in *storagemigrationv1alpha1.GroupVersionResource, out *storagemigration.GroupVersionResource, s conversion.Scope) error { - out.Group = in.Group - out.Version = in.Version - out.Resource = in.Resource - return nil -} - -// Convert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource is an autogenerated conversion function. -func Convert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource(in *storagemigrationv1alpha1.GroupVersionResource, out *storagemigration.GroupVersionResource, s conversion.Scope) error { - return autoConvert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource(in, out, s) -} - -func autoConvert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource(in *storagemigration.GroupVersionResource, out *storagemigrationv1alpha1.GroupVersionResource, s conversion.Scope) error { - out.Group = in.Group - out.Version = in.Version - out.Resource = in.Resource - return nil -} - -// Convert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource is an autogenerated conversion function. -func Convert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource(in *storagemigration.GroupVersionResource, out *storagemigrationv1alpha1.GroupVersionResource, s conversion.Scope) error { - return autoConvert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource(in, out, s) -} - -func autoConvert_v1alpha1_MigrationCondition_To_storagemigration_MigrationCondition(in *storagemigrationv1alpha1.MigrationCondition, out *storagemigration.MigrationCondition, s conversion.Scope) error { - out.Type = storagemigration.MigrationConditionType(in.Type) - out.Status = v1.ConditionStatus(in.Status) - out.LastUpdateTime = in.LastUpdateTime - out.Reason = in.Reason - out.Message = in.Message - return nil -} - -// Convert_v1alpha1_MigrationCondition_To_storagemigration_MigrationCondition is an autogenerated conversion function. -func Convert_v1alpha1_MigrationCondition_To_storagemigration_MigrationCondition(in *storagemigrationv1alpha1.MigrationCondition, out *storagemigration.MigrationCondition, s conversion.Scope) error { - return autoConvert_v1alpha1_MigrationCondition_To_storagemigration_MigrationCondition(in, out, s) -} - -func autoConvert_storagemigration_MigrationCondition_To_v1alpha1_MigrationCondition(in *storagemigration.MigrationCondition, out *storagemigrationv1alpha1.MigrationCondition, s conversion.Scope) error { - out.Type = storagemigrationv1alpha1.MigrationConditionType(in.Type) - out.Status = v1.ConditionStatus(in.Status) - out.LastUpdateTime = in.LastUpdateTime - out.Reason = in.Reason - out.Message = in.Message - return nil -} - -// Convert_storagemigration_MigrationCondition_To_v1alpha1_MigrationCondition is an autogenerated conversion function. -func Convert_storagemigration_MigrationCondition_To_v1alpha1_MigrationCondition(in *storagemigration.MigrationCondition, out *storagemigrationv1alpha1.MigrationCondition, s conversion.Scope) error { - return autoConvert_storagemigration_MigrationCondition_To_v1alpha1_MigrationCondition(in, out, s) -} - -func autoConvert_v1alpha1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in *storagemigrationv1alpha1.StorageVersionMigration, out *storagemigration.StorageVersionMigration, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - if err := Convert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(&in.Status, &out.Status, s); err != nil { - return err - } - return nil -} - -// Convert_v1alpha1_StorageVersionMigration_To_storagemigration_StorageVersionMigration is an autogenerated conversion function. -func Convert_v1alpha1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in *storagemigrationv1alpha1.StorageVersionMigration, out *storagemigration.StorageVersionMigration, s conversion.Scope) error { - return autoConvert_v1alpha1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in, out, s) -} - -func autoConvert_storagemigration_StorageVersionMigration_To_v1alpha1_StorageVersionMigration(in *storagemigration.StorageVersionMigration, out *storagemigrationv1alpha1.StorageVersionMigration, s conversion.Scope) error { - out.ObjectMeta = in.ObjectMeta - if err := Convert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec(&in.Spec, &out.Spec, s); err != nil { - return err - } - if err := Convert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus(&in.Status, &out.Status, s); err != nil { - return err - } - return nil -} - -// Convert_storagemigration_StorageVersionMigration_To_v1alpha1_StorageVersionMigration is an autogenerated conversion function. -func Convert_storagemigration_StorageVersionMigration_To_v1alpha1_StorageVersionMigration(in *storagemigration.StorageVersionMigration, out *storagemigrationv1alpha1.StorageVersionMigration, s conversion.Scope) error { - return autoConvert_storagemigration_StorageVersionMigration_To_v1alpha1_StorageVersionMigration(in, out, s) -} - -func autoConvert_v1alpha1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in *storagemigrationv1alpha1.StorageVersionMigrationList, out *storagemigration.StorageVersionMigrationList, s conversion.Scope) error { - out.ListMeta = in.ListMeta - out.Items = *(*[]storagemigration.StorageVersionMigration)(unsafe.Pointer(&in.Items)) - return nil -} - -// Convert_v1alpha1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList is an autogenerated conversion function. -func Convert_v1alpha1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in *storagemigrationv1alpha1.StorageVersionMigrationList, out *storagemigration.StorageVersionMigrationList, s conversion.Scope) error { - return autoConvert_v1alpha1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in, out, s) -} - -func autoConvert_storagemigration_StorageVersionMigrationList_To_v1alpha1_StorageVersionMigrationList(in *storagemigration.StorageVersionMigrationList, out *storagemigrationv1alpha1.StorageVersionMigrationList, s conversion.Scope) error { - out.ListMeta = in.ListMeta - out.Items = *(*[]storagemigrationv1alpha1.StorageVersionMigration)(unsafe.Pointer(&in.Items)) - return nil -} - -// Convert_storagemigration_StorageVersionMigrationList_To_v1alpha1_StorageVersionMigrationList is an autogenerated conversion function. -func Convert_storagemigration_StorageVersionMigrationList_To_v1alpha1_StorageVersionMigrationList(in *storagemigration.StorageVersionMigrationList, out *storagemigrationv1alpha1.StorageVersionMigrationList, s conversion.Scope) error { - return autoConvert_storagemigration_StorageVersionMigrationList_To_v1alpha1_StorageVersionMigrationList(in, out, s) -} - -func autoConvert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in *storagemigrationv1alpha1.StorageVersionMigrationSpec, out *storagemigration.StorageVersionMigrationSpec, s conversion.Scope) error { - if err := Convert_v1alpha1_GroupVersionResource_To_storagemigration_GroupVersionResource(&in.Resource, &out.Resource, s); err != nil { - return err - } - out.ContinueToken = in.ContinueToken - return nil -} - -// Convert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec is an autogenerated conversion function. -func Convert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in *storagemigrationv1alpha1.StorageVersionMigrationSpec, out *storagemigration.StorageVersionMigrationSpec, s conversion.Scope) error { - return autoConvert_v1alpha1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in, out, s) -} - -func autoConvert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec(in *storagemigration.StorageVersionMigrationSpec, out *storagemigrationv1alpha1.StorageVersionMigrationSpec, s conversion.Scope) error { - if err := Convert_storagemigration_GroupVersionResource_To_v1alpha1_GroupVersionResource(&in.Resource, &out.Resource, s); err != nil { - return err - } - out.ContinueToken = in.ContinueToken - return nil -} - -// Convert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec is an autogenerated conversion function. -func Convert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec(in *storagemigration.StorageVersionMigrationSpec, out *storagemigrationv1alpha1.StorageVersionMigrationSpec, s conversion.Scope) error { - return autoConvert_storagemigration_StorageVersionMigrationSpec_To_v1alpha1_StorageVersionMigrationSpec(in, out, s) -} - -func autoConvert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in *storagemigrationv1alpha1.StorageVersionMigrationStatus, out *storagemigration.StorageVersionMigrationStatus, s conversion.Scope) error { - out.Conditions = *(*[]storagemigration.MigrationCondition)(unsafe.Pointer(&in.Conditions)) - out.ResourceVersion = in.ResourceVersion - return nil -} - -// Convert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus is an autogenerated conversion function. -func Convert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in *storagemigrationv1alpha1.StorageVersionMigrationStatus, out *storagemigration.StorageVersionMigrationStatus, s conversion.Scope) error { - return autoConvert_v1alpha1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in, out, s) -} - -func autoConvert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus(in *storagemigration.StorageVersionMigrationStatus, out *storagemigrationv1alpha1.StorageVersionMigrationStatus, s conversion.Scope) error { - out.Conditions = *(*[]storagemigrationv1alpha1.MigrationCondition)(unsafe.Pointer(&in.Conditions)) - out.ResourceVersion = in.ResourceVersion - return nil -} - -// Convert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus is an autogenerated conversion function. -func Convert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus(in *storagemigration.StorageVersionMigrationStatus, out *storagemigrationv1alpha1.StorageVersionMigrationStatus, s conversion.Scope) error { - return autoConvert_storagemigration_StorageVersionMigrationStatus_To_v1alpha1_StorageVersionMigrationStatus(in, out, s) -} diff --git a/pkg/apis/storagemigration/v1beta1/doc.go b/pkg/apis/storagemigration/v1beta1/doc.go new file mode 100644 index 0000000000000..d414f83bd5b0a --- /dev/null +++ b/pkg/apis/storagemigration/v1beta1/doc.go @@ -0,0 +1,23 @@ +/* +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. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/storagemigration +// +k8s:conversion-gen-external-types=k8s.io/api/storagemigration/v1beta1 +// +groupName=storagemigration.k8s.io +// +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=k8s.io/api/storagemigration/v1beta1 + +package v1beta1 diff --git a/pkg/apis/storagemigration/v1beta1/register.go b/pkg/apis/storagemigration/v1beta1/register.go new file mode 100644 index 0000000000000..e8747fc637a86 --- /dev/null +++ b/pkg/apis/storagemigration/v1beta1/register.go @@ -0,0 +1,39 @@ +/* +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 v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + + svmv1beta1 "k8s.io/api/storagemigration/v1beta1" +) + +// GroupName is the group name use in this package +const GroupName = "storagemigration.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &svmv1beta1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) diff --git a/pkg/apis/storagemigration/v1beta1/zz_generated.conversion.go b/pkg/apis/storagemigration/v1beta1/zz_generated.conversion.go new file mode 100644 index 0000000000000..9b1b160f71c6c --- /dev/null +++ b/pkg/apis/storagemigration/v1beta1/zz_generated.conversion.go @@ -0,0 +1,178 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1beta1 + +import ( + unsafe "unsafe" + + storagemigrationv1beta1 "k8s.io/api/storagemigration/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + storagemigration "k8s.io/kubernetes/pkg/apis/storagemigration" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*storagemigrationv1beta1.StorageVersionMigration)(nil), (*storagemigration.StorageVersionMigration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(a.(*storagemigrationv1beta1.StorageVersionMigration), b.(*storagemigration.StorageVersionMigration), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigration)(nil), (*storagemigrationv1beta1.StorageVersionMigration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_storagemigration_StorageVersionMigration_To_v1beta1_StorageVersionMigration(a.(*storagemigration.StorageVersionMigration), b.(*storagemigrationv1beta1.StorageVersionMigration), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigrationv1beta1.StorageVersionMigrationList)(nil), (*storagemigration.StorageVersionMigrationList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(a.(*storagemigrationv1beta1.StorageVersionMigrationList), b.(*storagemigration.StorageVersionMigrationList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationList)(nil), (*storagemigrationv1beta1.StorageVersionMigrationList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_storagemigration_StorageVersionMigrationList_To_v1beta1_StorageVersionMigrationList(a.(*storagemigration.StorageVersionMigrationList), b.(*storagemigrationv1beta1.StorageVersionMigrationList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigrationv1beta1.StorageVersionMigrationSpec)(nil), (*storagemigration.StorageVersionMigrationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(a.(*storagemigrationv1beta1.StorageVersionMigrationSpec), b.(*storagemigration.StorageVersionMigrationSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationSpec)(nil), (*storagemigrationv1beta1.StorageVersionMigrationSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec(a.(*storagemigration.StorageVersionMigrationSpec), b.(*storagemigrationv1beta1.StorageVersionMigrationSpec), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigrationv1beta1.StorageVersionMigrationStatus)(nil), (*storagemigration.StorageVersionMigrationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(a.(*storagemigrationv1beta1.StorageVersionMigrationStatus), b.(*storagemigration.StorageVersionMigrationStatus), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*storagemigration.StorageVersionMigrationStatus)(nil), (*storagemigrationv1beta1.StorageVersionMigrationStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus(a.(*storagemigration.StorageVersionMigrationStatus), b.(*storagemigrationv1beta1.StorageVersionMigrationStatus), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in *storagemigrationv1beta1.StorageVersionMigration, out *storagemigration.StorageVersionMigration, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_StorageVersionMigration_To_storagemigration_StorageVersionMigration is an autogenerated conversion function. +func Convert_v1beta1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in *storagemigrationv1beta1.StorageVersionMigration, out *storagemigration.StorageVersionMigration, s conversion.Scope) error { + return autoConvert_v1beta1_StorageVersionMigration_To_storagemigration_StorageVersionMigration(in, out, s) +} + +func autoConvert_storagemigration_StorageVersionMigration_To_v1beta1_StorageVersionMigration(in *storagemigration.StorageVersionMigration, out *storagemigrationv1beta1.StorageVersionMigration, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_storagemigration_StorageVersionMigration_To_v1beta1_StorageVersionMigration is an autogenerated conversion function. +func Convert_storagemigration_StorageVersionMigration_To_v1beta1_StorageVersionMigration(in *storagemigration.StorageVersionMigration, out *storagemigrationv1beta1.StorageVersionMigration, s conversion.Scope) error { + return autoConvert_storagemigration_StorageVersionMigration_To_v1beta1_StorageVersionMigration(in, out, s) +} + +func autoConvert_v1beta1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in *storagemigrationv1beta1.StorageVersionMigrationList, out *storagemigration.StorageVersionMigrationList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]storagemigration.StorageVersionMigration)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList is an autogenerated conversion function. +func Convert_v1beta1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in *storagemigrationv1beta1.StorageVersionMigrationList, out *storagemigration.StorageVersionMigrationList, s conversion.Scope) error { + return autoConvert_v1beta1_StorageVersionMigrationList_To_storagemigration_StorageVersionMigrationList(in, out, s) +} + +func autoConvert_storagemigration_StorageVersionMigrationList_To_v1beta1_StorageVersionMigrationList(in *storagemigration.StorageVersionMigrationList, out *storagemigrationv1beta1.StorageVersionMigrationList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]storagemigrationv1beta1.StorageVersionMigration)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_storagemigration_StorageVersionMigrationList_To_v1beta1_StorageVersionMigrationList is an autogenerated conversion function. +func Convert_storagemigration_StorageVersionMigrationList_To_v1beta1_StorageVersionMigrationList(in *storagemigration.StorageVersionMigrationList, out *storagemigrationv1beta1.StorageVersionMigrationList, s conversion.Scope) error { + return autoConvert_storagemigration_StorageVersionMigrationList_To_v1beta1_StorageVersionMigrationList(in, out, s) +} + +func autoConvert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in *storagemigrationv1beta1.StorageVersionMigrationSpec, out *storagemigration.StorageVersionMigrationSpec, s conversion.Scope) error { + out.Resource = in.Resource + return nil +} + +// Convert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec is an autogenerated conversion function. +func Convert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in *storagemigrationv1beta1.StorageVersionMigrationSpec, out *storagemigration.StorageVersionMigrationSpec, s conversion.Scope) error { + return autoConvert_v1beta1_StorageVersionMigrationSpec_To_storagemigration_StorageVersionMigrationSpec(in, out, s) +} + +func autoConvert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec(in *storagemigration.StorageVersionMigrationSpec, out *storagemigrationv1beta1.StorageVersionMigrationSpec, s conversion.Scope) error { + out.Resource = in.Resource + return nil +} + +// Convert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec is an autogenerated conversion function. +func Convert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec(in *storagemigration.StorageVersionMigrationSpec, out *storagemigrationv1beta1.StorageVersionMigrationSpec, s conversion.Scope) error { + return autoConvert_storagemigration_StorageVersionMigrationSpec_To_v1beta1_StorageVersionMigrationSpec(in, out, s) +} + +func autoConvert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in *storagemigrationv1beta1.StorageVersionMigrationStatus, out *storagemigration.StorageVersionMigrationStatus, s conversion.Scope) error { + out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + out.ResourceVersion = in.ResourceVersion + return nil +} + +// Convert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus is an autogenerated conversion function. +func Convert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in *storagemigrationv1beta1.StorageVersionMigrationStatus, out *storagemigration.StorageVersionMigrationStatus, s conversion.Scope) error { + return autoConvert_v1beta1_StorageVersionMigrationStatus_To_storagemigration_StorageVersionMigrationStatus(in, out, s) +} + +func autoConvert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus(in *storagemigration.StorageVersionMigrationStatus, out *storagemigrationv1beta1.StorageVersionMigrationStatus, s conversion.Scope) error { + out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions)) + out.ResourceVersion = in.ResourceVersion + return nil +} + +// Convert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus is an autogenerated conversion function. +func Convert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus(in *storagemigration.StorageVersionMigrationStatus, out *storagemigrationv1beta1.StorageVersionMigrationStatus, s conversion.Scope) error { + return autoConvert_storagemigration_StorageVersionMigrationStatus_To_v1beta1_StorageVersionMigrationStatus(in, out, s) +} diff --git a/pkg/apis/storagemigration/v1alpha1/zz_generated.defaults.go b/pkg/apis/storagemigration/v1beta1/zz_generated.defaults.go similarity index 98% rename from pkg/apis/storagemigration/v1alpha1/zz_generated.defaults.go rename to pkg/apis/storagemigration/v1beta1/zz_generated.defaults.go index 5070cb91b90f1..198b5be4af534 100644 --- a/pkg/apis/storagemigration/v1alpha1/zz_generated.defaults.go +++ b/pkg/apis/storagemigration/v1beta1/zz_generated.defaults.go @@ -19,7 +19,7 @@ limitations under the License. // Code generated by defaulter-gen. DO NOT EDIT. -package v1alpha1 +package v1beta1 import ( runtime "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/apis/storagemigration/validation/validation.go b/pkg/apis/storagemigration/validation/validation.go index 404acac980a80..efcfb3618e755 100644 --- a/pkg/apis/storagemigration/validation/validation.go +++ b/pkg/apis/storagemigration/validation/validation.go @@ -18,18 +18,17 @@ package validation import ( "fmt" - "regexp" - "strconv" + "strings" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apimachinery/pkg/util/validation" + "k8s.io/apimachinery/pkg/util/resourceversion" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/apis/storagemigration" - corev1 "k8s.io/api/core/v1" + metaconditions "k8s.io/apimachinery/pkg/api/meta" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" + utilvalidation "k8s.io/apimachinery/pkg/util/validation" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" ) @@ -37,8 +36,20 @@ func ValidateStorageVersionMigration(svm *storagemigration.StorageVersionMigrati allErrs := field.ErrorList{} allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&svm.ObjectMeta, false, apimachineryvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))...) - allErrs = checkAndAppendError(allErrs, field.NewPath("spec", "resource", "resource"), svm.Spec.Resource.Resource, "resource is required") - allErrs = checkAndAppendError(allErrs, field.NewPath("spec", "resource", "version"), svm.Spec.Resource.Version, "version is required") + if len(svm.Spec.Resource.Resource) == 0 { + allErrs = append(allErrs, field.Required(field.NewPath("spec", "resource", "resource"), "resource is required to be set")) + } else { + // Same validations as APIService, Group must be a DNS1123 Subdomain and Resource must be DNS1035 + if errs := utilvalidation.IsDNS1035Label(svm.Spec.Resource.Resource); len(errs) > 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "resource", "resource"), svm.Spec.Resource.Resource, strings.Join(errs, ","))) + } + } + + if len(svm.Spec.Resource.Group) != 0 { + if errs := utilvalidation.IsDNS1123Subdomain(svm.Spec.Resource.Group); len(errs) > 0 { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "resource", "group"), svm.Spec.Resource.Group, strings.Join(errs, ","))) + } + } return allErrs } @@ -47,16 +58,8 @@ func ValidateStorageVersionMigrationUpdate(newSVMBundle, oldSVMBundle *storagemi allErrs := ValidateStorageVersionMigration(newSVMBundle) allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&newSVMBundle.ObjectMeta, &oldSVMBundle.ObjectMeta, field.NewPath("metadata"))...) - // prevent changes to the group, version and resource - if newSVMBundle.Spec.Resource.Group != oldSVMBundle.Spec.Resource.Group { - allErrs = append(allErrs, field.Invalid(field.NewPath("group"), newSVMBundle.Spec.Resource.Group, "field is immutable")) - } - if newSVMBundle.Spec.Resource.Version != oldSVMBundle.Spec.Resource.Version { - allErrs = append(allErrs, field.Invalid(field.NewPath("version"), newSVMBundle.Spec.Resource.Version, "field is immutable")) - } - if newSVMBundle.Spec.Resource.Resource != oldSVMBundle.Spec.Resource.Resource { - allErrs = append(allErrs, field.Invalid(field.NewPath("resource"), newSVMBundle.Spec.Resource.Resource, "field is immutable")) - } + // prevent changes to the spec + allErrs = append(allErrs, apivalidation.ValidateImmutableField(newSVMBundle.Spec, oldSVMBundle.Spec, field.NewPath("spec"))...) return allErrs } @@ -67,14 +70,15 @@ func ValidateStorageVersionMigrationStatusUpdate(newSVMBundle, oldSVMBundle *sto fldPath := field.NewPath("status") // resource version should be a non-negative integer - rvInt, err := convertResourceVersionToInt(newSVMBundle.Status.ResourceVersion) - if err != nil { + cmp, err := resourceversion.CompareResourceVersion(newSVMBundle.Status.ResourceVersion, newSVMBundle.Status.ResourceVersion) + if err != nil || cmp != 0 { + if err == nil { + err = fmt.Errorf("unable to compare resource versions, %s is not equal to %s", newSVMBundle.Status.ResourceVersion, newSVMBundle.Status.ResourceVersion) + } allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newSVMBundle.Status.ResourceVersion, err.Error())) } - allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(rvInt, fldPath.Child("resourceVersion"))...) - // TODO: after switching to metav1.Conditions in beta replace this validation with metav1.ValidateConditions - allErrs = append(allErrs, validateConditions(newSVMBundle.Status.Conditions, fldPath.Child("conditions"))...) + allErrs = append(allErrs, metav1validation.ValidateConditions(newSVMBundle.Status.Conditions, fldPath.Child("conditions"))...) // resource version should not change once it has been set if len(oldSVMBundle.Status.ResourceVersion) != 0 && oldSVMBundle.Status.ResourceVersion != newSVMBundle.Status.ResourceVersion { @@ -108,120 +112,25 @@ func ValidateStorageVersionMigrationStatusUpdate(newSVMBundle, oldSVMBundle *sto } func isSuccessful(svm *storagemigration.StorageVersionMigration) bool { - successCondition := getCondition(svm, storagemigration.MigrationSucceeded) - if successCondition != nil && successCondition.Status == corev1.ConditionTrue { + successCondition := metaconditions.FindStatusCondition(svm.Status.Conditions, string(storagemigration.MigrationSucceeded)) + if successCondition != nil && successCondition.Status == metav1.ConditionTrue { return true } return false } func isFailed(svm *storagemigration.StorageVersionMigration) bool { - failedCondition := getCondition(svm, storagemigration.MigrationFailed) - if failedCondition != nil && failedCondition.Status == corev1.ConditionTrue { + failedCondition := metaconditions.FindStatusCondition(svm.Status.Conditions, string(storagemigration.MigrationFailed)) + if failedCondition != nil && failedCondition.Status == metav1.ConditionTrue { return true } return false } func isRunning(svm *storagemigration.StorageVersionMigration) bool { - runningCondition := getCondition(svm, storagemigration.MigrationRunning) - if runningCondition != nil && runningCondition.Status == corev1.ConditionTrue { + runningCondition := metaconditions.FindStatusCondition(svm.Status.Conditions, string(storagemigration.MigrationRunning)) + if runningCondition != nil && runningCondition.Status == metav1.ConditionTrue { return true } return false } - -func getCondition(svm *storagemigration.StorageVersionMigration, conditionType storagemigration.MigrationConditionType) *storagemigration.MigrationCondition { - for _, c := range svm.Status.Conditions { - if c.Type == conditionType { - return &c - } - } - - return nil -} - -func validateConditions(conditions []storagemigration.MigrationCondition, fldPath *field.Path) field.ErrorList { - var allErrs field.ErrorList - - conditionTypeToFirstIndex := map[string]int{} - for i, condition := range conditions { - if _, ok := conditionTypeToFirstIndex[string(condition.Type)]; ok { - allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("type"), condition.Type)) - } else { - conditionTypeToFirstIndex[string(condition.Type)] = i - } - - allErrs = append(allErrs, validateCondition(condition, fldPath.Index(i))...) - } - - return allErrs -} - -func validateCondition(condition storagemigration.MigrationCondition, fldPath *field.Path) field.ErrorList { - var allErrs field.ErrorList - var validConditionStatuses = sets.NewString(string(metav1.ConditionTrue), string(metav1.ConditionFalse), string(metav1.ConditionUnknown)) - - // type is set and is a valid format - allErrs = append(allErrs, metav1validation.ValidateLabelName(string(condition.Type), fldPath.Child("type"))...) - - // status is set and is an accepted value - if !validConditionStatuses.Has(string(condition.Status)) { - allErrs = append(allErrs, field.NotSupported(fldPath.Child("status"), condition.Status, validConditionStatuses.List())) - } - - if condition.LastUpdateTime.IsZero() { - allErrs = append(allErrs, field.Required(fldPath.Child("lastTransitionTime"), "")) - } - - if len(condition.Reason) == 0 { - allErrs = append(allErrs, field.Required(fldPath.Child("reason"), "")) - } else { - for _, currErr := range isValidConditionReason(condition.Reason) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("reason"), condition.Reason, currErr)) - } - - const maxReasonLen int = 1 * 1024 // 1024 - if len(condition.Reason) > maxReasonLen { - allErrs = append(allErrs, field.TooLong(fldPath.Child("reason"), "" /*unused*/, maxReasonLen)) - } - } - - const maxMessageLen int = 32 * 1024 // 32768 - if len(condition.Message) > maxMessageLen { - allErrs = append(allErrs, field.TooLong(fldPath.Child("message"), "" /*unused*/, maxMessageLen)) - } - - return allErrs -} -func isValidConditionReason(value string) []string { - const conditionReasonFmt string = "[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?" - const conditionReasonErrMsg string = "a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_'" - var conditionReasonRegexp = regexp.MustCompile("^" + conditionReasonFmt + "$") - - if !conditionReasonRegexp.MatchString(value) { - return []string{validation.RegexError(conditionReasonErrMsg, conditionReasonFmt, "my_name", "MY_NAME", "MyName", "ReasonA,ReasonB", "ReasonA:ReasonB")} - } - return nil -} - -func checkAndAppendError(allErrs field.ErrorList, fieldPath *field.Path, value string, message string) field.ErrorList { - if len(value) == 0 { - allErrs = append(allErrs, field.Required(fieldPath, message)) - } - return allErrs -} - -func convertResourceVersionToInt(rv string) (int64, error) { - // initial value of RV is expected to be empty, which means the resource version is not set - if len(rv) == 0 { - return 0, nil - } - - resourceVersion, err := strconv.ParseInt(rv, 10, 64) - if err != nil { - return 0, fmt.Errorf("failed to parse resource version %q: %w", rv, err) - } - - return resourceVersion, nil -} diff --git a/pkg/apis/storagemigration/validation/validation_test.go b/pkg/apis/storagemigration/validation/validation_test.go index bb1e95aa3d429..e6dabecca30df 100644 --- a/pkg/apis/storagemigration/validation/validation_test.go +++ b/pkg/apis/storagemigration/validation/validation_test.go @@ -17,6 +17,8 @@ limitations under the License. package validation import ( + "fmt" + "strings" "testing" "k8s.io/kubernetes/pkg/apis/storagemigration" @@ -24,6 +26,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const dns1035ErrMsg = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')" +const dns1123ErrMsg = "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')" + // TestValidateStorageVersionMigration tests the ValidateStorageVersionMigration function func TestValidateStorageVersionMigration(t *testing.T) { tests := []struct { @@ -38,9 +43,8 @@ func TestValidateStorageVersionMigration(t *testing.T) { Name: "test-svm", }, Spec: storagemigration.StorageVersionMigrationSpec{ - Resource: storagemigration.GroupVersionResource{ + Resource: metav1.GroupResource{ Group: "non-empty", - Version: "non-empty", Resource: "non-empty", }, }, @@ -54,14 +58,13 @@ func TestValidateStorageVersionMigration(t *testing.T) { Name: "test-svm", }, Spec: storagemigration.StorageVersionMigrationSpec{ - Resource: storagemigration.GroupVersionResource{ + Resource: metav1.GroupResource{ Group: "", - Version: "", Resource: "", }, }, }, - errorString: "[spec.resource.resource: Required value: resource is required, spec.resource.version: Required value: version is required]", + errorString: "spec.resource.resource: Required value: resource is required to be set", }, { name: "when resource is empty", @@ -70,30 +73,43 @@ func TestValidateStorageVersionMigration(t *testing.T) { Name: "test-svm", }, Spec: storagemigration.StorageVersionMigrationSpec{ - Resource: storagemigration.GroupVersionResource{ + Resource: metav1.GroupResource{ Group: "non-empty", - Version: "non-empty", Resource: "", }, }, }, - errorString: "spec.resource.resource: Required value: resource is required", + errorString: "spec.resource.resource: Required value: resource is required to be set", }, { - name: "when version is empty", + name: "when resource is invalid", svm: &storagemigration.StorageVersionMigration{ ObjectMeta: metav1.ObjectMeta{ Name: "test-svm", }, Spec: storagemigration.StorageVersionMigrationSpec{ - Resource: storagemigration.GroupVersionResource{ + Resource: metav1.GroupResource{ Group: "non-empty", - Version: "", + Resource: ".", + }, + }, + }, + errorString: fmt.Sprintf("spec.resource.resource: Invalid value: \".\": %s", dns1035ErrMsg), + }, + { + name: "when group is invalid", + svm: &storagemigration.StorageVersionMigration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-svm", + }, + Spec: storagemigration.StorageVersionMigrationSpec{ + Resource: metav1.GroupResource{ + Group: "-", Resource: "non-empty", }, }, }, - errorString: "spec.resource.version: Required value: version is required", + errorString: fmt.Sprintf("spec.resource.group: Invalid value: \"-\": %s", dns1123ErrMsg), }, } @@ -114,3 +130,212 @@ func TestValidateStorageVersionMigration(t *testing.T) { }) } } +func TestValidateStorageVersionMigrationUpdate(t *testing.T) { + validSVM := &storagemigration.StorageVersionMigration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-svm", + ResourceVersion: "1", + }, + Spec: storagemigration.StorageVersionMigrationSpec{ + Resource: metav1.GroupResource{ + Group: "example.com", + Resource: "myresources", + }, + }, + } + + tests := []struct { + name string + newSVM *storagemigration.StorageVersionMigration + oldSVM *storagemigration.StorageVersionMigration + errorSubstring string + }{ + { + name: "valid update (no change)", + newSVM: validSVM.DeepCopy(), + oldSVM: validSVM.DeepCopy(), + errorSubstring: "", + }, + { + name: "valid update (metadata change)", + newSVM: func() *storagemigration.StorageVersionMigration { + svm := validSVM.DeepCopy() + svm.ObjectMeta.Labels = map[string]string{"a": "b"} + return svm + }(), + oldSVM: validSVM.DeepCopy(), + errorSubstring: "", + }, + { + name: "invalid update (spec changed)", + newSVM: func() *storagemigration.StorageVersionMigration { + svm := validSVM.DeepCopy() + svm.Spec.Resource.Group = "new.example.com" + return svm + }(), + oldSVM: validSVM.DeepCopy(), + errorSubstring: "spec: Invalid value", + }, + { + name: "invalid update (new object is invalid)", + newSVM: func() *storagemigration.StorageVersionMigration { + svm := validSVM.DeepCopy() + svm.Spec.Resource.Resource = "" + return svm + }(), + oldSVM: validSVM.DeepCopy(), + errorSubstring: "spec.resource.resource: Required value", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + errors := ValidateStorageVersionMigrationUpdate(test.newSVM, test.oldSVM) + + if test.errorSubstring == "" { + if len(errors) != 0 { + t.Errorf("Expected no error, got %s", errors.ToAggregate().Error()) + } + } else { + if len(errors) == 0 { + t.Errorf("Expected error containing %q, got no error", test.errorSubstring) + } else if !strings.Contains(errors.ToAggregate().Error(), test.errorSubstring) { + t.Errorf("Expected error containing %q, got %s", test.errorSubstring, errors.ToAggregate().Error()) + } + } + }) + } +} + +var ( + // Add a time variable + now = metav1.Now() + + // Add LastTransitionTime to all conditions + runningCond = metav1.Condition{Type: string(storagemigration.MigrationRunning), Status: metav1.ConditionTrue, Reason: "Running", LastTransitionTime: now} + succeededCond = metav1.Condition{Type: string(storagemigration.MigrationSucceeded), Status: metav1.ConditionTrue, Reason: "Succeeded", LastTransitionTime: now} + failedCond = metav1.Condition{Type: string(storagemigration.MigrationFailed), Status: metav1.ConditionTrue, Reason: "Failed", LastTransitionTime: now} + + runningFalseCond = metav1.Condition{Type: string(storagemigration.MigrationRunning), Status: metav1.ConditionFalse, Reason: "NotRunning", LastTransitionTime: now} + succeededFalseCond = metav1.Condition{Type: string(storagemigration.MigrationSucceeded), Status: metav1.ConditionFalse, Reason: "NotSucceeded", LastTransitionTime: now} + failedFalseCond = metav1.Condition{Type: string(storagemigration.MigrationFailed), Status: metav1.ConditionFalse, Reason: "NotFailed", LastTransitionTime: now} +) + +func newTestSVM(rv string, conditions ...metav1.Condition) *storagemigration.StorageVersionMigration { + return &storagemigration.StorageVersionMigration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-svm", + ResourceVersion: "1", + }, + Spec: storagemigration.StorageVersionMigrationSpec{ + Resource: metav1.GroupResource{ + Group: "example.com", + Resource: "myresources", + }, + }, + Status: storagemigration.StorageVersionMigrationStatus{ + ResourceVersion: rv, + Conditions: conditions, + }, + } +} + +func TestValidateStorageVersionMigrationStatusUpdate(t *testing.T) { + tests := []struct { + name string + newSVM *storagemigration.StorageVersionMigration + oldSVM *storagemigration.StorageVersionMigration + errorSubstring string + }{ + { + name: "valid: initial status set with RV", + newSVM: newTestSVM("123", runningCond), + oldSVM: newTestSVM(""), + errorSubstring: "", + }, + { + name: "valid: transition running to succeeded", + newSVM: newTestSVM("123", runningFalseCond, succeededCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "", + }, + { + name: "valid: transition running to failed", + newSVM: newTestSVM("123", runningFalseCond, failedCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "", + }, + { + name: "valid: succeeded stays succeeded", + newSVM: newTestSVM("123", succeededCond), + oldSVM: newTestSVM("123", succeededCond), + errorSubstring: "", + }, + { + name: "valid: failed stays failed", + newSVM: newTestSVM("123", failedCond), + oldSVM: newTestSVM("123", failedCond), + errorSubstring: "", + }, + { + name: "invalid: resource version change", + newSVM: newTestSVM("456", runningCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "status.resourceVersion: Invalid value: \"456\": field is immutable", + }, + { + name: "invalid: bad resource version format", + newSVM: newTestSVM("abc", runningCond), + oldSVM: newTestSVM(""), + errorSubstring: "status.resourceVersion: Invalid value: \"abc\": resource version is not well formed: abc", + }, + { + name: "invalid: both succeeded and failed are true", + newSVM: newTestSVM("123", succeededCond, failedCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "Both success and failed conditions cannot be true at the same time", + }, + { + name: "invalid: both succeeded and running are true", + newSVM: newTestSVM("123", succeededCond, runningCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "Running condition cannot be true when success condition is true", + }, + { + name: "invalid: both failed and running are true", + newSVM: newTestSVM("123", failedCond, runningCond), + oldSVM: newTestSVM("123", runningCond), + errorSubstring: "Running condition cannot be true when failed condition is true", + }, + { + name: "invalid: succeeded changed from true to false", + newSVM: newTestSVM("123", succeededFalseCond), + oldSVM: newTestSVM("123", succeededCond), + errorSubstring: "Success condition cannot be set to false once it is true", + }, + { + name: "invalid: failed changed from true to false", + newSVM: newTestSVM("123", failedFalseCond), + oldSVM: newTestSVM("123", failedCond), + errorSubstring: "Failed condition cannot be set to false once it is true", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + errors := ValidateStorageVersionMigrationStatusUpdate(test.newSVM, test.oldSVM) + + if test.errorSubstring == "" { + if len(errors) != 0 { + t.Errorf("Expected no error, got %s", errors.ToAggregate().Error()) + } + } else { + if len(errors) == 0 { + t.Errorf("Expected error containing %q, got no error", test.errorSubstring) + } else if !strings.Contains(errors.ToAggregate().Error(), test.errorSubstring) { + t.Errorf("Expected error containing %q, got %s", test.errorSubstring, errors.ToAggregate().Error()) + } + } + }) + } +} diff --git a/pkg/apis/storagemigration/zz_generated.deepcopy.go b/pkg/apis/storagemigration/zz_generated.deepcopy.go index 59b4383e110c5..d31dd7a293cb6 100644 --- a/pkg/apis/storagemigration/zz_generated.deepcopy.go +++ b/pkg/apis/storagemigration/zz_generated.deepcopy.go @@ -22,42 +22,10 @@ limitations under the License. package storagemigration import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GroupVersionResource) DeepCopyInto(out *GroupVersionResource) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupVersionResource. -func (in *GroupVersionResource) DeepCopy() *GroupVersionResource { - if in == nil { - return nil - } - out := new(GroupVersionResource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MigrationCondition) DeepCopyInto(out *MigrationCondition) { - *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MigrationCondition. -func (in *MigrationCondition) DeepCopy() *MigrationCondition { - if in == nil { - return nil - } - out := new(MigrationCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StorageVersionMigration) DeepCopyInto(out *StorageVersionMigration) { *out = *in @@ -141,7 +109,7 @@ func (in *StorageVersionMigrationStatus) DeepCopyInto(out *StorageVersionMigrati *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]MigrationCondition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/controller/OWNERS b/pkg/controller/OWNERS index 88cecd8f96060..be732c048fbc9 100644 --- a/pkg/controller/OWNERS +++ b/pkg/controller/OWNERS @@ -12,6 +12,7 @@ reviewers: - andrewsykim - deads2k - cheftako + - jefftree - sig-apps-reviewers labels: - sig/apps diff --git a/pkg/controller/apis/config/types.go b/pkg/controller/apis/config/types.go index 880a4fd4928b2..3be7f98a5c4e6 100644 --- a/pkg/controller/apis/config/types.go +++ b/pkg/controller/apis/config/types.go @@ -25,6 +25,7 @@ import ( cronjobconfig "k8s.io/kubernetes/pkg/controller/cronjob/config" daemonconfig "k8s.io/kubernetes/pkg/controller/daemon/config" deploymentconfig "k8s.io/kubernetes/pkg/controller/deployment/config" + devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" endpointconfig "k8s.io/kubernetes/pkg/controller/endpoint/config" endpointsliceconfig "k8s.io/kubernetes/pkg/controller/endpointslice/config" endpointslicemirroringconfig "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config" @@ -62,6 +63,9 @@ type KubeControllerManagerConfiguration struct { // AttachDetachControllerConfiguration holds configuration for // AttachDetachController related features. AttachDetachController attachdetachconfig.AttachDetachControllerConfiguration + // CronJobControllerConfiguration holds configuration for CronJobController + // related features. + CronJobController cronjobconfig.CronJobControllerConfiguration // CSRSigningControllerConfiguration holds configuration for // CSRSigningController related features. CSRSigningController csrsigningconfig.CSRSigningControllerConfiguration @@ -71,9 +75,8 @@ type KubeControllerManagerConfiguration struct { // DeploymentControllerConfiguration holds configuration for // DeploymentController related features. DeploymentController deploymentconfig.DeploymentControllerConfiguration - // StatefulSetControllerConfiguration holds configuration for - // StatefulSetController related features. - StatefulSetController statefulsetconfig.StatefulSetControllerConfiguration + // DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller. + DeviceTaintEvictionController devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration // DeprecatedControllerConfiguration holds configuration for some deprecated // features. DeprecatedController DeprecatedControllerConfiguration @@ -96,9 +99,6 @@ type KubeControllerManagerConfiguration struct { HPAController poautosclerconfig.HPAControllerConfiguration // JobControllerConfiguration holds configuration for JobController related features. JobController jobconfig.JobControllerConfiguration - // CronJobControllerConfiguration holds configuration for CronJobController - // related features. - CronJobController cronjobconfig.CronJobControllerConfiguration // LegacySATokenCleanerConfiguration holds configuration for LegacySATokenCleaner related features. LegacySATokenCleaner serviceaccountconfig.LegacySATokenCleanerConfiguration // NamespaceControllerConfiguration holds configuration for NamespaceController @@ -130,6 +130,9 @@ type KubeControllerManagerConfiguration struct { // ServiceControllerConfiguration holds configuration for ServiceController // related features. ServiceController serviceconfig.ServiceControllerConfiguration + // StatefulSetControllerConfiguration holds configuration for + // StatefulSetController related features. + StatefulSetController statefulsetconfig.StatefulSetControllerConfiguration // TTLAfterFinishedControllerConfiguration holds configuration for // TTLAfterFinishedController related features. TTLAfterFinishedController ttlafterfinishedconfig.TTLAfterFinishedControllerConfiguration diff --git a/pkg/controller/apis/config/v1alpha1/defaults.go b/pkg/controller/apis/config/v1alpha1/defaults.go index c5a6ccef62fc9..d0d4fd160ae45 100644 --- a/pkg/controller/apis/config/v1alpha1/defaults.go +++ b/pkg/controller/apis/config/v1alpha1/defaults.go @@ -25,6 +25,7 @@ import ( cronjobconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/cronjob/config/v1alpha1" daemonconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/daemon/config/v1alpha1" deploymentconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/deployment/config/v1alpha1" + devicetaintevictionconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/devicetainteviction/config/v1alpha1" endpointconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpoint/config/v1alpha1" endpointsliceconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslice/config/v1alpha1" endpointslicemirroringconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config/v1alpha1" @@ -71,6 +72,8 @@ func SetDefaults_KubeControllerManagerConfiguration(obj *kubectrlmgrconfigv1alph daemonconfigv1alpha1.RecommendedDefaultDaemonSetControllerConfiguration(&obj.DaemonSetController) // Use the default RecommendedDefaultDeploymentControllerConfiguration options deploymentconfigv1alpha1.RecommendedDefaultDeploymentControllerConfiguration(&obj.DeploymentController) + // Use the default RecommendedDefaultDeviceTaintEvictionControllerConfiguration options + devicetaintevictionconfigv1alpha1.RecommendedDefaultDeviceTaintEvictionControllerConfiguration(&obj.DeviceTaintEvictionController) // Use the default RecommendedDefaultStatefulSetControllerConfiguration options statefulsetconfigv1alpha1.RecommendedDefaultStatefulSetControllerConfiguration(&obj.StatefulSetController) // Use the default RecommendedDefaultEndpointControllerConfiguration options diff --git a/pkg/controller/apis/config/v1alpha1/zz_generated.conversion.go b/pkg/controller/apis/config/v1alpha1/zz_generated.conversion.go index 383177ba91488..b9b276389aabd 100644 --- a/pkg/controller/apis/config/v1alpha1/zz_generated.conversion.go +++ b/pkg/controller/apis/config/v1alpha1/zz_generated.conversion.go @@ -34,6 +34,7 @@ import ( cronjobconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/cronjob/config/v1alpha1" daemonconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/daemon/config/v1alpha1" deploymentconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/deployment/config/v1alpha1" + devicetaintevictionconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/devicetainteviction/config/v1alpha1" endpointconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpoint/config/v1alpha1" endpointsliceconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslice/config/v1alpha1" endpointslicemirroringconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config/v1alpha1" @@ -224,6 +225,9 @@ func autoConvert_v1alpha1_KubeControllerManagerConfiguration_To_config_KubeContr if err := validatingadmissionpolicystatusconfigv1alpha1.Convert_v1alpha1_ValidatingAdmissionPolicyStatusControllerConfiguration_To_config_ValidatingAdmissionPolicyStatusControllerConfiguration(&in.ValidatingAdmissionPolicyStatusController, &out.ValidatingAdmissionPolicyStatusController, s); err != nil { return err } + if err := devicetaintevictionconfigv1alpha1.Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(&in.DeviceTaintEvictionController, &out.DeviceTaintEvictionController, s); err != nil { + return err + } return nil } @@ -242,6 +246,9 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr if err := attachdetachconfigv1alpha1.Convert_config_AttachDetachControllerConfiguration_To_v1alpha1_AttachDetachControllerConfiguration(&in.AttachDetachController, &out.AttachDetachController, s); err != nil { return err } + if err := cronjobconfigv1alpha1.Convert_config_CronJobControllerConfiguration_To_v1alpha1_CronJobControllerConfiguration(&in.CronJobController, &out.CronJobController, s); err != nil { + return err + } if err := signerconfigv1alpha1.Convert_config_CSRSigningControllerConfiguration_To_v1alpha1_CSRSigningControllerConfiguration(&in.CSRSigningController, &out.CSRSigningController, s); err != nil { return err } @@ -251,7 +258,7 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr if err := deploymentconfigv1alpha1.Convert_config_DeploymentControllerConfiguration_To_v1alpha1_DeploymentControllerConfiguration(&in.DeploymentController, &out.DeploymentController, s); err != nil { return err } - if err := statefulsetconfigv1alpha1.Convert_config_StatefulSetControllerConfiguration_To_v1alpha1_StatefulSetControllerConfiguration(&in.StatefulSetController, &out.StatefulSetController, s); err != nil { + if err := devicetaintevictionconfigv1alpha1.Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(&in.DeviceTaintEvictionController, &out.DeviceTaintEvictionController, s); err != nil { return err } if err := Convert_config_DeprecatedControllerConfiguration_To_v1alpha1_DeprecatedControllerConfiguration(&in.DeprecatedController, &out.DeprecatedController, s); err != nil { @@ -278,9 +285,6 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr if err := jobconfigv1alpha1.Convert_config_JobControllerConfiguration_To_v1alpha1_JobControllerConfiguration(&in.JobController, &out.JobController, s); err != nil { return err } - if err := cronjobconfigv1alpha1.Convert_config_CronJobControllerConfiguration_To_v1alpha1_CronJobControllerConfiguration(&in.CronJobController, &out.CronJobController, s); err != nil { - return err - } if err := serviceaccountconfigv1alpha1.Convert_config_LegacySATokenCleanerConfiguration_To_v1alpha1_LegacySATokenCleanerConfiguration(&in.LegacySATokenCleaner, &out.LegacySATokenCleaner, s); err != nil { return err } @@ -314,6 +318,9 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr if err := serviceconfigv1alpha1.Convert_config_ServiceControllerConfiguration_To_v1alpha1_ServiceControllerConfiguration(&in.ServiceController, &out.ServiceController, s); err != nil { return err } + if err := statefulsetconfigv1alpha1.Convert_config_StatefulSetControllerConfiguration_To_v1alpha1_StatefulSetControllerConfiguration(&in.StatefulSetController, &out.StatefulSetController, s); err != nil { + return err + } if err := ttlafterfinishedconfigv1alpha1.Convert_config_TTLAfterFinishedControllerConfiguration_To_v1alpha1_TTLAfterFinishedControllerConfiguration(&in.TTLAfterFinishedController, &out.TTLAfterFinishedController, s); err != nil { return err } diff --git a/pkg/controller/apis/config/zz_generated.deepcopy.go b/pkg/controller/apis/config/zz_generated.deepcopy.go index 0393df7d99e2f..82d905bbe210f 100644 --- a/pkg/controller/apis/config/zz_generated.deepcopy.go +++ b/pkg/controller/apis/config/zz_generated.deepcopy.go @@ -48,10 +48,11 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa in.Generic.DeepCopyInto(&out.Generic) out.KubeCloudShared = in.KubeCloudShared out.AttachDetachController = in.AttachDetachController + out.CronJobController = in.CronJobController out.CSRSigningController = in.CSRSigningController out.DaemonSetController = in.DaemonSetController out.DeploymentController = in.DeploymentController - out.StatefulSetController = in.StatefulSetController + out.DeviceTaintEvictionController = in.DeviceTaintEvictionController out.DeprecatedController = in.DeprecatedController out.EndpointController = in.EndpointController out.EndpointSliceController = in.EndpointSliceController @@ -60,7 +61,6 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa in.GarbageCollectorController.DeepCopyInto(&out.GarbageCollectorController) out.HPAController = in.HPAController out.JobController = in.JobController - out.CronJobController = in.CronJobController out.LegacySATokenCleaner = in.LegacySATokenCleaner out.NamespaceController = in.NamespaceController out.NodeIPAMController = in.NodeIPAMController @@ -72,6 +72,7 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa out.ResourceQuotaController = in.ResourceQuotaController out.SAController = in.SAController out.ServiceController = in.ServiceController + out.StatefulSetController = in.StatefulSetController out.TTLAfterFinishedController = in.TTLAfterFinishedController out.ValidatingAdmissionPolicyStatusController = in.ValidatingAdmissionPolicyStatusController return diff --git a/pkg/controller/bootstrap/bootstrapsigner.go b/pkg/controller/bootstrap/bootstrapsigner.go index b0472eddeee78..be18ed2f0ab01 100644 --- a/pkg/controller/bootstrap/bootstrapsigner.go +++ b/pkg/controller/bootstrap/bootstrapsigner.go @@ -19,6 +19,7 @@ package bootstrap import ( "context" "strings" + "sync" "time" "k8s.io/klog/v2" @@ -155,19 +156,26 @@ func NewSigner(cl clientset.Interface, secrets informers.SecretInformer, configM // Run runs controller loops and returns when they are done func (e *Signer) Run(ctx context.Context) { - // Shut down queues defer utilruntime.HandleCrash() - defer e.syncQueue.ShutDown() - if !cache.WaitForNamedCacheSync("bootstrap_signer", ctx.Done(), e.configMapSynced, e.secretSynced) { + logger := klog.FromContext(ctx) + logger.V(5).Info("Starting") + + var wg sync.WaitGroup + defer func() { + logger.V(1).Info("Shutting down") + e.syncQueue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, e.configMapSynced, e.secretSynced) { return } - logger := klog.FromContext(ctx) - logger.V(5).Info("Starting workers") - go wait.UntilWithContext(ctx, e.serviceConfigMapQueue, 0) + wg.Go(func() { + wait.UntilWithContext(ctx, e.serviceConfigMapQueue, 0) + }) <-ctx.Done() - logger.V(1).Info("Shutting down") } func (e *Signer) pokeConfigMapSync() { diff --git a/pkg/controller/bootstrap/tokencleaner.go b/pkg/controller/bootstrap/tokencleaner.go index 52fa81bd49e6a..a949cbbe69657 100644 --- a/pkg/controller/bootstrap/tokencleaner.go +++ b/pkg/controller/bootstrap/tokencleaner.go @@ -19,6 +19,7 @@ package bootstrap import ( "context" "fmt" + "sync" "time" v1 "k8s.io/api/core/v1" @@ -111,18 +112,24 @@ func NewTokenCleaner(cl clientset.Interface, secrets coreinformers.SecretInforme // Run runs controller loops and returns when they are done func (tc *TokenCleaner) Run(ctx context.Context) { defer utilruntime.HandleCrash() - defer tc.queue.ShutDown() logger := klog.FromContext(ctx) logger.Info("Starting token cleaner controller") - defer logger.Info("Shutting down token cleaner controller") - if !cache.WaitForNamedCacheSync("token_cleaner", ctx.Done(), tc.secretSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down token cleaner controller") + tc.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, tc.secretSynced) { return } - go wait.UntilWithContext(ctx, tc.worker, 10*time.Second) - + wg.Go(func() { + wait.UntilWithContext(ctx, tc.worker, 10*time.Second) + }) <-ctx.Done() } diff --git a/pkg/controller/certificates/certificate_controller.go b/pkg/controller/certificates/certificate_controller.go index 1306eef892b9e..dbfa3e4ad5429 100644 --- a/pkg/controller/certificates/certificate_controller.go +++ b/pkg/controller/certificates/certificate_controller.go @@ -21,6 +21,7 @@ package certificates import ( "context" "fmt" + "sync" "time" "golang.org/x/time/rate" @@ -114,20 +115,26 @@ func NewCertificateController( // Run the main goroutine responsible for watching and syncing jobs. func (cc *CertificateController) Run(ctx context.Context, workers int) { defer utilruntime.HandleCrash() - defer cc.queue.ShutDown() logger := klog.FromContext(ctx) logger.Info("Starting certificate controller", "name", cc.name) - defer logger.Info("Shutting down certificate controller", "name", cc.name) - if !cache.WaitForNamedCacheSync(fmt.Sprintf("certificate-%s", cc.name), ctx.Done(), cc.csrsSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down certificate controller", "name", cc.name) + cc.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, cc.csrsSynced) { return } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, cc.worker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, cc.worker, time.Second) + }) } - <-ctx.Done() } diff --git a/pkg/controller/certificates/cleaner/cleaner.go b/pkg/controller/certificates/cleaner/cleaner.go index 32c0830d1d580..b63321de958e7 100644 --- a/pkg/controller/certificates/cleaner/cleaner.go +++ b/pkg/controller/certificates/cleaner/cleaner.go @@ -25,10 +25,9 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "sync" "time" - "k8s.io/klog/v2" - capi "k8s.io/api/certificates/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -37,6 +36,7 @@ import ( certificatesinformers "k8s.io/client-go/informers/certificates/v1" csrclient "k8s.io/client-go/kubernetes/typed/certificates/v1" certificateslisters "k8s.io/client-go/listers/certificates/v1" + "k8s.io/klog/v2" ) const ( @@ -81,12 +81,18 @@ func (ccc *CSRCleanerController) Run(ctx context.Context, workers int) { logger := klog.FromContext(ctx) logger.Info("Starting CSR cleaner controller") - defer logger.Info("Shutting down CSR cleaner controller") + + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down CSR cleaner controller") + wg.Wait() + }() for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, ccc.worker, pollingInterval) + wg.Go(func() { + wait.UntilWithContext(ctx, ccc.worker, pollingInterval) + }) } - <-ctx.Done() } @@ -107,7 +113,12 @@ func (ccc *CSRCleanerController) worker(ctx context.Context) { func (ccc *CSRCleanerController) handle(ctx context.Context, csr *capi.CertificateSigningRequest) error { logger := klog.FromContext(ctx) - if isIssuedPastDeadline(logger, csr) || isDeniedPastDeadline(logger, csr) || isFailedPastDeadline(logger, csr) || isPendingPastDeadline(logger, csr) || isIssuedExpired(logger, csr) { + if isIssuedPastDeadline(logger, csr) || + isDeniedPastDeadline(logger, csr) || + isFailedPastDeadline(logger, csr) || + isPendingPastDeadline(logger, csr) || + isIssuedExpired(logger, csr) || + isApprovedUnissuedPastDeadline(logger, csr) { if err := ccc.csrClient.Delete(ctx, csr.Name, metav1.DeleteOptions{}); err != nil { return fmt.Errorf("unable to delete CSR %q: %v", csr.Name, err) } @@ -170,8 +181,11 @@ func isFailedPastDeadline(logger klog.Logger, csr *capi.CertificateSigningReques // creation time of the CSR is passed the deadline that issued requests are // maintained for. func isIssuedPastDeadline(logger klog.Logger, csr *capi.CertificateSigningRequest) bool { + if !isIssued(csr) { + return false + } for _, c := range csr.Status.Conditions { - if c.Type == capi.CertificateApproved && isIssued(csr) && isOlderThan(c.LastUpdateTime, approvedExpiration) { + if c.Type == capi.CertificateApproved && isOlderThan(c.LastUpdateTime, approvedExpiration) { logger.Info("Cleaning CSR as it is more than approvedExpiration duration old and approved.", "csr", csr.Name, "approvedExpiration", approvedExpiration) return true } @@ -179,6 +193,22 @@ func isIssuedPastDeadline(logger klog.Logger, csr *capi.CertificateSigningReques return false } +// isApprovedUnissuedPastDeadline checks if the certificate has an Approved status but +// no certificate has been issued, and the approval time has passed the deadline +// that pending requests are maintained for. +func isApprovedUnissuedPastDeadline(logger klog.Logger, csr *capi.CertificateSigningRequest) bool { + if isIssued(csr) { + return false + } + for _, c := range csr.Status.Conditions { + if c.Type == capi.CertificateApproved && isOlderThan(c.LastUpdateTime, pendingExpiration) { + logger.Info("Cleaning CSR as it is approved but unissued for more than pendingExpiration duration.", "csr", csr.Name, "pendingExpiration", pendingExpiration) + return true + } + } + return false +} + // isOlderThan checks that t is a non-zero and older than d from time.Now(). func isOlderThan(t metav1.Time, d time.Duration) bool { return !t.IsZero() && time.Since(t.Time) > d diff --git a/pkg/controller/certificates/cleaner/cleaner_test.go b/pkg/controller/certificates/cleaner/cleaner_test.go index 6faeeb7bdf0b8..bb7c3a7d172a0 100644 --- a/pkg/controller/certificates/cleaner/cleaner_test.go +++ b/pkg/controller/certificates/cleaner/cleaner_test.go @@ -171,6 +171,30 @@ func TestCleanerWithApprovedExpiredCSR(t *testing.T) { []capi.CertificateSigningRequestCondition{}, []string{"delete"}, }, + { + "delete approved unissued past deadline", + metav1.NewTime(time.Now().Add(-1 * time.Minute)), + nil, + []capi.CertificateSigningRequestCondition{ + { + Type: capi.CertificateApproved, + LastUpdateTime: metav1.NewTime(time.Now().Add(-25 * time.Hour)), + }, + }, + []string{"delete"}, + }, + { + "no delete approved unissued not past deadline", + metav1.NewTime(time.Now().Add(-1 * time.Minute)), + nil, + []capi.CertificateSigningRequestCondition{ + { + Type: capi.CertificateApproved, + LastUpdateTime: metav1.NewTime(time.Now().Add(-5 * time.Hour)), + }, + }, + []string{}, + }, { "no delete approved not passed deadline unexpired", metav1.NewTime(time.Now().Add(-1 * time.Minute)), diff --git a/pkg/controller/certificates/cleaner/pcrcleaner.go b/pkg/controller/certificates/cleaner/pcrcleaner.go index 9a2107c744f75..93a154136b62f 100644 --- a/pkg/controller/certificates/cleaner/pcrcleaner.go +++ b/pkg/controller/certificates/cleaner/pcrcleaner.go @@ -21,15 +21,15 @@ import ( "fmt" "time" - certsv1alpha1 "k8s.io/api/certificates/v1alpha1" + certsv1beta1 "k8s.io/api/certificates/v1beta1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" - certinformersv1alpha1 "k8s.io/client-go/informers/certificates/v1alpha1" + certinformersv1beta1 "k8s.io/client-go/informers/certificates/v1beta1" "k8s.io/client-go/kubernetes" - certlistersv1alpha1 "k8s.io/client-go/listers/certificates/v1alpha1" + certlistersv1beta1 "k8s.io/client-go/listers/certificates/v1beta1" "k8s.io/klog/v2" "k8s.io/utils/clock" "k8s.io/utils/ptr" @@ -39,7 +39,7 @@ import ( // minutes. type PCRCleanerController struct { client kubernetes.Interface - pcrLister certlistersv1alpha1.PodCertificateRequestLister + pcrLister certlistersv1beta1.PodCertificateRequestLister clock clock.PassiveClock threshold time.Duration pollingInterval time.Duration @@ -48,7 +48,7 @@ type PCRCleanerController struct { // NewPCRCleanerController creates a PCRCleanerController. func NewPCRCleanerController( client kubernetes.Interface, - pcrLister certinformersv1alpha1.PodCertificateRequestInformer, + pcrLister certinformersv1beta1.PodCertificateRequestInformer, clock clock.PassiveClock, threshold time.Duration, pollingInterval time.Duration, @@ -69,9 +69,7 @@ func (c *PCRCleanerController) Run(ctx context.Context, workers int) { logger.Info("Starting PodCertificateRequest cleaner controller") defer logger.Info("Shutting down PodCertificateRequest cleaner controller") - go wait.UntilWithContext(ctx, c.worker, c.pollingInterval) - - <-ctx.Done() + wait.UntilWithContext(ctx, c.worker, c.pollingInterval) } func (c *PCRCleanerController) worker(ctx context.Context) { @@ -87,7 +85,7 @@ func (c *PCRCleanerController) worker(ctx context.Context) { } } -func (c PCRCleanerController) handle(ctx context.Context, pcr *certsv1alpha1.PodCertificateRequest) error { +func (c PCRCleanerController) handle(ctx context.Context, pcr *certsv1beta1.PodCertificateRequest) error { if c.clock.Now().Before(pcr.ObjectMeta.CreationTimestamp.Time.Add(c.threshold)) { return nil } @@ -98,7 +96,7 @@ func (c PCRCleanerController) handle(ctx context.Context, pcr *certsv1alpha1.Pod }, } - err := c.client.CertificatesV1alpha1().PodCertificateRequests(pcr.ObjectMeta.Namespace).Delete(ctx, pcr.ObjectMeta.Name, opts) + err := c.client.CertificatesV1beta1().PodCertificateRequests(pcr.ObjectMeta.Namespace).Delete(ctx, pcr.ObjectMeta.Name, opts) if k8serrors.IsNotFound(err) { // This is OK, we don't care if someone else already deleted it. return nil diff --git a/pkg/controller/certificates/cleaner/pcrcleaner_test.go b/pkg/controller/certificates/cleaner/pcrcleaner_test.go index a544401432d7b..1eca4f42e81d1 100644 --- a/pkg/controller/certificates/cleaner/pcrcleaner_test.go +++ b/pkg/controller/certificates/cleaner/pcrcleaner_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - certsv1alpha1 "k8s.io/api/certificates/v1alpha1" + certsv1beta1 "k8s.io/api/certificates/v1beta1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -42,18 +42,18 @@ func TestPCRCleaner(t *testing.T) { testCases := []struct { desc string - pcr *certsv1alpha1.PodCertificateRequest + pcr *certsv1beta1.PodCertificateRequest wantErrRecognizer func(error) bool }{ { desc: "Pending request within the threshold should be left alone", - pcr: &certsv1alpha1.PodCertificateRequest{ + pcr: &certsv1beta1.PodCertificateRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", Name: "bar", CreationTimestamp: metav1.NewTime(mustParseRFC3339(t, "2025-01-01T00:15:01Z")), }, - Spec: certsv1alpha1.PodCertificateRequestSpec{ + Spec: certsv1beta1.PodCertificateRequestSpec{ SignerName: "foo.com/abc", PodName: "pod-1", PodUID: types.UID(podUID1), @@ -70,13 +70,13 @@ func TestPCRCleaner(t *testing.T) { }, { desc: "Pending request outside the threshold should be deleted", - pcr: &certsv1alpha1.PodCertificateRequest{ + pcr: &certsv1beta1.PodCertificateRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", Name: "bar", CreationTimestamp: metav1.NewTime(mustParseRFC3339(t, "2025-01-01T00:14:59Z")), }, - Spec: certsv1alpha1.PodCertificateRequestSpec{ + Spec: certsv1beta1.PodCertificateRequestSpec{ SignerName: "foo.com/abc", PodName: "pod-1", PodUID: types.UID(podUID1), @@ -93,13 +93,13 @@ func TestPCRCleaner(t *testing.T) { }, { desc: "Terminal request within the threshold should be left alone", - pcr: &certsv1alpha1.PodCertificateRequest{ + pcr: &certsv1beta1.PodCertificateRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", Name: "bar", CreationTimestamp: metav1.NewTime(mustParseRFC3339(t, "2025-01-01T00:15:01Z")), }, - Spec: certsv1alpha1.PodCertificateRequestSpec{ + Spec: certsv1beta1.PodCertificateRequestSpec{ SignerName: "foo.com/abc", PodName: "pod-1", PodUID: types.UID(podUID1), @@ -111,10 +111,10 @@ func TestPCRCleaner(t *testing.T) { PKIXPublicKey: pubPKIX1, ProofOfPossession: proof1, }, - Status: certsv1alpha1.PodCertificateRequestStatus{ + Status: certsv1beta1.PodCertificateRequestStatus{ Conditions: []metav1.Condition{ { - Type: certsv1alpha1.PodCertificateRequestConditionTypeDenied, + Type: certsv1beta1.PodCertificateRequestConditionTypeDenied, Status: metav1.ConditionTrue, Reason: "Foo", Message: "abc", @@ -126,13 +126,13 @@ func TestPCRCleaner(t *testing.T) { }, { desc: "Terminal request outside the threshold should be deleted", - pcr: &certsv1alpha1.PodCertificateRequest{ + pcr: &certsv1beta1.PodCertificateRequest{ ObjectMeta: metav1.ObjectMeta{ Namespace: "foo", Name: "bar", CreationTimestamp: metav1.NewTime(mustParseRFC3339(t, "2025-01-01T00:14:59Z")), }, - Spec: certsv1alpha1.PodCertificateRequestSpec{ + Spec: certsv1beta1.PodCertificateRequestSpec{ SignerName: "foo.com/abc", PodName: "pod-1", PodUID: types.UID(podUID1), @@ -144,10 +144,10 @@ func TestPCRCleaner(t *testing.T) { PKIXPublicKey: pubPKIX1, ProofOfPossession: proof1, }, - Status: certsv1alpha1.PodCertificateRequestStatus{ + Status: certsv1beta1.PodCertificateRequestStatus{ Conditions: []metav1.Condition{ { - Type: certsv1alpha1.PodCertificateRequestConditionTypeDenied, + Type: certsv1beta1.PodCertificateRequestConditionTypeDenied, Status: metav1.ConditionTrue, Reason: "Foo", Message: "abc", @@ -173,7 +173,7 @@ func TestPCRCleaner(t *testing.T) { // Simulate a pass of the cleaner worker by listing all PCRs and // calling handle() on them. - pcrList, err := kc.CertificatesV1alpha1().PodCertificateRequests(metav1.NamespaceAll).List(ctx, metav1.ListOptions{}) + pcrList, err := kc.CertificatesV1beta1().PodCertificateRequests(metav1.NamespaceAll).List(ctx, metav1.ListOptions{}) if err != nil { t.Fatalf("Unexpected error listing PCRs: %v", err) } @@ -185,7 +185,7 @@ func TestPCRCleaner(t *testing.T) { // Now check on the test case's PCR, to see if it was deleted or not // according to our expectation. - _, err = kc.CertificatesV1alpha1().PodCertificateRequests(tc.pcr.ObjectMeta.Namespace).Get(ctx, tc.pcr.ObjectMeta.Name, metav1.GetOptions{}) + _, err = kc.CertificatesV1beta1().PodCertificateRequests(tc.pcr.ObjectMeta.Namespace).Get(ctx, tc.pcr.ObjectMeta.Name, metav1.GetOptions{}) if !tc.wantErrRecognizer(err) { t.Errorf("Bad error output: %v", err) } diff --git a/pkg/controller/certificates/clustertrustbundlepublisher/publisher.go b/pkg/controller/certificates/clustertrustbundlepublisher/publisher.go index f5293b0583ea9..f39ac0fa41217 100644 --- a/pkg/controller/certificates/clustertrustbundlepublisher/publisher.go +++ b/pkg/controller/certificates/clustertrustbundlepublisher/publisher.go @@ -21,6 +21,7 @@ import ( "crypto/sha256" "fmt" "strings" + "sync" "time" certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1" @@ -272,22 +273,29 @@ func (p *ClusterTrustBundlePublisher[T]) caContentChangedListener() dynamiccerti func (p *ClusterTrustBundlePublisher[T]) Run(ctx context.Context) { defer utilruntime.HandleCrash() - defer p.queue.ShutDown() logger := klog.FromContext(ctx) logger.Info("Starting ClusterTrustBundle CA cert publisher controller") - defer logger.Info("Shutting down ClusterTrustBundle CA cert publisher controller") - go p.ctbInformer.Run(ctx.Done()) + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down ClusterTrustBundle CA cert publisher controller") + p.queue.ShutDown() + wg.Wait() + }() + + wg.Go(func() { + p.ctbInformer.Run(ctx.Done()) + }) - if !cache.WaitForNamedCacheSync("cluster trust bundle", ctx.Done(), p.ctbListerSynced) { + if !cache.WaitForNamedCacheSyncWithContext(ctx, p.ctbListerSynced) { return } - // init the signer syncer p.queue.Add("") - go wait.UntilWithContext(ctx, p.runWorker(), time.Second) - + wg.Go(func() { + wait.UntilWithContext(ctx, p.runWorker(), time.Second) + }) <-ctx.Done() } diff --git a/pkg/controller/certificates/rootcacertpublisher/publisher.go b/pkg/controller/certificates/rootcacertpublisher/publisher.go index 36127e883e3ad..6c2bc0bfd8305 100644 --- a/pkg/controller/certificates/rootcacertpublisher/publisher.go +++ b/pkg/controller/certificates/rootcacertpublisher/publisher.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "sync" "time" v1 "k8s.io/api/core/v1" @@ -101,20 +102,26 @@ type Publisher struct { // Run starts process func (c *Publisher) Run(ctx context.Context, workers int) { defer utilruntime.HandleCrash() - defer c.queue.ShutDown() logger := klog.FromContext(ctx) logger.Info("Starting root CA cert publisher controller") - defer logger.Info("Shutting down root CA cert publisher controller") - if !cache.WaitForNamedCacheSync("crt configmap", ctx.Done(), c.cmListerSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down root CA cert publisher controller") + c.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, c.cmListerSynced) { return } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, c.runWorker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, c.runWorker, time.Second) + }) } - <-ctx.Done() } diff --git a/pkg/controller/certificates/signer/signer.go b/pkg/controller/certificates/signer/signer.go index 0f0d146e3414d..9695b4d2f6305 100644 --- a/pkg/controller/certificates/signer/signer.go +++ b/pkg/controller/certificates/signer/signer.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "sync" "time" capi "k8s.io/api/certificates/v1" @@ -111,9 +112,14 @@ func NewCSRSigningController( // Run the main goroutine responsible for watching and syncing jobs. func (c *CSRSigningController) Run(ctx context.Context, workers int) { - go c.dynamicCertReloader.Run(ctx, workers) - - c.certificateController.Run(ctx, workers) + var wg sync.WaitGroup + wg.Go(func() { + c.dynamicCertReloader.Run(ctx, workers) + }) + wg.Go(func() { + c.certificateController.Run(ctx, workers) + }) + wg.Wait() } type isRequestForSignerFunc func(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error) diff --git a/pkg/controller/clusterroleaggregation/clusterroleaggregation_controller.go b/pkg/controller/clusterroleaggregation/clusterroleaggregation_controller.go index bebc45160ebaa..2b27994f8e7b7 100644 --- a/pkg/controller/clusterroleaggregation/clusterroleaggregation_controller.go +++ b/pkg/controller/clusterroleaggregation/clusterroleaggregation_controller.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "sort" + "sync" "time" rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1" @@ -188,20 +189,26 @@ func ruleExists(haystack []rbacv1.PolicyRule, needle rbacv1.PolicyRule) bool { // Run starts the controller and blocks until stopCh is closed. func (c *ClusterRoleAggregationController) Run(ctx context.Context, workers int) { defer utilruntime.HandleCrash() - defer c.queue.ShutDown() logger := klog.FromContext(ctx) logger.Info("Starting ClusterRoleAggregator controller") - defer logger.Info("Shutting down ClusterRoleAggregator controller") - if !cache.WaitForNamedCacheSync("ClusterRoleAggregator", ctx.Done(), c.clusterRolesSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down ClusterRoleAggregator controller") + c.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, c.clusterRolesSynced) { return } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, c.runWorker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, c.runWorker, time.Second) + }) } - <-ctx.Done() } diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 20fb8056418a3..63a7e2768ae33 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -863,8 +863,10 @@ func TestSortingActivePodsWithRanks(t *testing.T) { for i, test := range inequalityTests { t.Run(fmt.Sprintf("Inequality tests %d", i), func(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDeletionCost, !test.disablePodDeletioncost) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LogarithmicScaleDown, !test.disableLogarithmicScaleDown) + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.PodDeletionCost: !test.disablePodDeletioncost, + features.LogarithmicScaleDown: !test.disableLogarithmicScaleDown, + }) podsWithRanks := ActivePodsWithRanks{ Pods: []*v1.Pod{test.lesser.pod, test.greater.pod}, diff --git a/pkg/controller/cronjob/cronjob_controllerv2.go b/pkg/controller/cronjob/cronjob_controllerv2.go index f67bd466ba213..89ebd0b098998 100644 --- a/pkg/controller/cronjob/cronjob_controllerv2.go +++ b/pkg/controller/cronjob/cronjob_controllerv2.go @@ -22,10 +22,9 @@ import ( "reflect" "sort" "strings" + "sync" "time" - "github.com/robfig/cron/v3" - batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -48,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/cronjob/metrics" jobutil "k8s.io/kubernetes/pkg/controller/job/util" + "k8s.io/kubernetes/pkg/util/parsers" "k8s.io/utils/ptr" ) @@ -139,20 +139,25 @@ func (jm *ControllerV2) Run(ctx context.Context, workers int) { jm.broadcaster.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: jm.kubeClient.CoreV1().Events("")}) defer jm.broadcaster.Shutdown() - defer jm.queue.ShutDown() - logger := klog.FromContext(ctx) logger.Info("Starting cronjob controller v2") - defer logger.Info("Shutting down cronjob controller v2") - if !cache.WaitForNamedCacheSync("cronjob", ctx.Done(), jm.jobListerSynced, jm.cronJobListerSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down cronjob controller v2") + jm.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, jm.jobListerSynced, jm.cronJobListerSynced) { return } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, jm.worker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, jm.worker, time.Second) + }) } - <-ctx.Done() } @@ -391,7 +396,7 @@ func (jm *ControllerV2) updateCronJob(logger klog.Logger, old interface{}, curr // the sync loop will essentially be a no-op for the already queued key with old schedule. if oldCJ.Spec.Schedule != newCJ.Spec.Schedule || !ptr.Equal(oldCJ.Spec.TimeZone, newCJ.Spec.TimeZone) { // schedule changed, change the requeue time, pass nil recorder so that syncCronJob will output any warnings - sched, err := cron.ParseStandard(formatSchedule(newCJ, nil)) + sched, err := parsers.ParseCronScheduleWithPanicRecovery(formatSchedule(newCJ, nil)) if err != nil { // this is likely a user error in defining the spec value // we should log the error and not reconcile this cronjob until an update to spec @@ -511,7 +516,7 @@ func (jm *ControllerV2) syncCronJob( return nil, updateStatus, nil } - sched, err := cron.ParseStandard(formatSchedule(cronJob, jm.recorder)) + sched, err := parsers.ParseCronScheduleWithPanicRecovery(formatSchedule(cronJob, jm.recorder)) if err != nil { // this is likely a user error in defining the spec value // we should log the error and not reconcile this cronjob until an update to spec diff --git a/pkg/controller/cronjob/utils.go b/pkg/controller/cronjob/utils.go index 78b4ab7ab0eeb..47d740763ad6f 100644 --- a/pkg/controller/cronjob/utils.go +++ b/pkg/controller/cronjob/utils.go @@ -27,10 +27,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/record" "k8s.io/klog/v2" - "k8s.io/kubernetes/pkg/features" "k8s.io/utils/ptr" ) @@ -245,19 +243,15 @@ func copyAnnotations(template *batchv1.JobTemplateSpec) labels.Set { // granularity of 1 minute for scheduling job. func getJobFromTemplate2(cj *batchv1.CronJob, scheduledTime time.Time) (*batchv1.Job, error) { labels := copyLabels(&cj.Spec.JobTemplate) - annotations := copyAnnotations(&cj.Spec.JobTemplate) // We want job names for a given nominal start time to have a deterministic name to avoid the same job being created twice name := getJobName(cj, scheduledTime) - - if utilfeature.DefaultFeatureGate.Enabled(features.CronJobsScheduledAnnotation) { - - timeZoneLocation, err := time.LoadLocation(ptr.Deref(cj.Spec.TimeZone, "")) - if err != nil { - return nil, err - } - // Append job creation timestamp to the cronJob annotations. The time will be in RFC3339 form. - annotations[batchv1.CronJobScheduledTimestampAnnotation] = scheduledTime.In(timeZoneLocation).Format(time.RFC3339) + annotations := copyAnnotations(&cj.Spec.JobTemplate) + timeZoneLocation, err := time.LoadLocation(ptr.Deref(cj.Spec.TimeZone, "")) + if err != nil { + return nil, err } + // Append job creation timestamp to the cronJob annotations. The time will be in RFC3339 form. + annotations[batchv1.CronJobScheduledTimestampAnnotation] = scheduledTime.In(timeZoneLocation).Format(time.RFC3339) job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controller/cronjob/utils_test.go b/pkg/controller/cronjob/utils_test.go index 7be27e23b0084..6d6e4820d735d 100644 --- a/pkg/controller/cronjob/utils_test.go +++ b/pkg/controller/cronjob/utils_test.go @@ -28,11 +28,8 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/record" - featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/klog/v2/ktesting" - "k8s.io/kubernetes/pkg/features" "k8s.io/utils/ptr" ) @@ -47,8 +44,6 @@ func TestGetJobFromTemplate2(t *testing.T) { scheduledTime = *topOfTheHour() ) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CronJobsScheduledAnnotation, true) - cj := batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", diff --git a/pkg/controller/daemon/daemon_controller.go b/pkg/controller/daemon/daemon_controller.go index 0ab4290ae55cf..5bcf013020741 100644 --- a/pkg/controller/daemon/daemon_controller.go +++ b/pkg/controller/daemon/daemon_controller.go @@ -33,6 +33,7 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + utilfeature "k8s.io/apiserver/pkg/util/feature" appsinformers "k8s.io/client-go/informers/apps/v1" coreinformers "k8s.io/client-go/informers/core/v1" clientset "k8s.io/client-go/kubernetes" @@ -51,6 +52,7 @@ import ( podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/daemon/util" + "k8s.io/kubernetes/pkg/features" ) const ( @@ -309,14 +311,18 @@ func (dsc *DaemonSetsController) Run(ctx context.Context, workers int) { dsc.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: dsc.kubeClient.CoreV1().Events("")}) defer dsc.eventBroadcaster.Shutdown() - defer dsc.queue.ShutDown() - defer dsc.nodeUpdateQueue.ShutDown() - logger := klog.FromContext(ctx) logger.Info("Starting daemon sets controller") - defer logger.Info("Shutting down daemon sets controller") - if !cache.WaitForNamedCacheSync("daemon sets", ctx.Done(), dsc.podStoreSynced, dsc.nodeStoreSynced, dsc.historyStoreSynced, dsc.dsStoreSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down daemon sets controller") + dsc.queue.ShutDown() + dsc.nodeUpdateQueue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, dsc.podStoreSynced, dsc.nodeStoreSynced, dsc.historyStoreSynced, dsc.dsStoreSynced) { return } if dsc.namespaceStoreSynced != nil { @@ -326,12 +332,16 @@ func (dsc *DaemonSetsController) Run(ctx context.Context, workers int) { } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, dsc.runWorker, time.Second) - go wait.UntilWithContext(ctx, dsc.runNodeUpdateWorker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, dsc.runWorker, time.Second) + }) + wg.Go(func() { + wait.UntilWithContext(ctx, dsc.runNodeUpdateWorker, time.Second) + }) } - - go wait.Until(dsc.failedPodsBackoff.GC, BackoffGCInterval, ctx.Done()) - + wg.Go(func() { + wait.Until(dsc.failedPodsBackoff.GC, BackoffGCInterval, ctx.Done()) + }) <-ctx.Done() } @@ -797,7 +807,7 @@ func (dsc *DaemonSetsController) podsShouldBeOnNode( hash string, ) (nodesNeedingDaemonPods, podsToDelete []string) { - shouldRun, shouldContinueRunning := dsc.nodeShouldRunDaemonPod(node, ds) + shouldRun, shouldContinueRunning := dsc.nodeShouldRunDaemonPod(logger, node, ds) daemonPods, exists := nodeToDaemonPods[node.Name] switch { @@ -1152,7 +1162,7 @@ func (dsc *DaemonSetsController) updateDaemonSetStatus(ctx context.Context, ds * var desiredNumberScheduled, currentNumberScheduled, numberMisscheduled, numberReady, updatedNumberScheduled, numberAvailable int now := dsc.failedPodsBackoff.Clock.Now() for _, node := range nodeList { - shouldRun, _ := dsc.nodeShouldRunDaemonPod(node, ds) + shouldRun, _ := dsc.nodeShouldRunDaemonPod(logger, node, ds) scheduled := len(nodeToDaemonPods[node.Name]) > 0 if shouldRun { @@ -1291,7 +1301,7 @@ func (dsc *DaemonSetsController) syncDaemonSet(ctx context.Context, key string) // - shouldContinueRunning: // Returns true when a daemonset should continue running on a node if a daemonset pod is already // running on that node. -func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) { +func NodeShouldRunDaemonPod(logger klog.Logger, node *v1.Node, ds *apps.DaemonSet) (bool, bool) { pod := NewPod(ds, node.Name) // If the daemon set specifies a node name, check that it matches with node.Name. @@ -1300,16 +1310,16 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) { } taints := node.Spec.Taints - fitsNodeName, fitsNodeAffinity, fitsTaints := predicates(pod, node, taints) + fitsNodeName, fitsNodeAffinity, fitsTaints := predicates(logger, pod, node, taints) if !fitsNodeName || !fitsNodeAffinity { return false, false } if !fitsTaints { // Scheduled daemon pods should continue running if they tolerate NoExecute taint. - _, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(taints, pod.Spec.Tolerations, func(t *v1.Taint) bool { + _, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(logger, taints, pod.Spec.Tolerations, func(t *v1.Taint) bool { return t.Effect == v1.TaintEffectNoExecute - }) + }, utilfeature.DefaultFeatureGate.Enabled(features.TaintTolerationComparisonOperators)) return false, !hasUntoleratedTaint } @@ -1317,13 +1327,13 @@ func NodeShouldRunDaemonPod(node *v1.Node, ds *apps.DaemonSet) (bool, bool) { } // predicates checks if a DaemonSet's pod can run on a node. -func predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeAffinity, fitsTaints bool) { +func predicates(logger klog.Logger, pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeAffinity, fitsTaints bool) { fitsNodeName = len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name // Ignore parsing errors for backwards compatibility. fitsNodeAffinity, _ = nodeaffinity.GetRequiredNodeAffinity(pod).Match(node) - _, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(taints, pod.Spec.Tolerations, func(t *v1.Taint) bool { + _, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(logger, taints, pod.Spec.Tolerations, func(t *v1.Taint) bool { return t.Effect == v1.TaintEffectNoExecute || t.Effect == v1.TaintEffectNoSchedule - }) + }, utilfeature.DefaultFeatureGate.Enabled(features.TaintTolerationComparisonOperators)) fitsTaints = !hasUntoleratedTaint return } @@ -1447,7 +1457,7 @@ func (dsc *DaemonSetsController) syncNodeUpdate(ctx context.Context, nodeName st } for _, ds := range dsList { - shouldRun, shouldContinueRunning := dsc.nodeShouldRunDaemonPod(node, ds) + shouldRun, shouldContinueRunning := dsc.nodeShouldRunDaemonPod(logger, node, ds) dsKey, err := controller.KeyFunc(ds) if err != nil { diff --git a/pkg/controller/daemon/daemon_controller_test.go b/pkg/controller/daemon/daemon_controller_test.go index 1f26d2a416b29..221ee3b9acbb0 100644 --- a/pkg/controller/daemon/daemon_controller_test.go +++ b/pkg/controller/daemon/daemon_controller_test.go @@ -2376,7 +2376,7 @@ func TestNodeShouldRunDaemonPod(t *testing.T) { node.Status.Conditions = append(node.Status.Conditions, c.nodeCondition...) node.Status.Allocatable = allocatableResources("100M", "1") node.Spec.Unschedulable = c.nodeUnschedulable - _, ctx := ktesting.NewTestContext(t) + logger, ctx := ktesting.NewTestContext(t) manager, _, _, err := newTestController(ctx) if err != nil { t.Fatalf("error creating DaemonSets controller: %v", err) @@ -2387,7 +2387,7 @@ func TestNodeShouldRunDaemonPod(t *testing.T) { manager.podStore.Add(p) } c.ds.Spec.UpdateStrategy = *strategy - shouldRun, shouldContinueRunning := NodeShouldRunDaemonPod(node, c.ds) + shouldRun, shouldContinueRunning := NodeShouldRunDaemonPod(logger, node, c.ds) if shouldRun != c.shouldRun { t.Errorf("[%v] strategy: %v, predicateName: %v expected shouldRun: %v, got: %v", i, c.ds.Spec.UpdateStrategy.Type, c.predicateName, c.shouldRun, shouldRun) diff --git a/pkg/controller/daemon/patch_nodeselector.go b/pkg/controller/daemon/patch_nodeselector.go index 356437dd52299..cd59823a5fa86 100644 --- a/pkg/controller/daemon/patch_nodeselector.go +++ b/pkg/controller/daemon/patch_nodeselector.go @@ -12,6 +12,7 @@ import ( coreinformers "k8s.io/client-go/informers/core/v1" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/util/flowcontrol" + "k8s.io/klog/v2" projectv1 "github.com/openshift/api/project/v1" ) @@ -42,8 +43,8 @@ func NewNodeSelectorAwareDaemonSetsController(ctx context.Context, openshiftDefa return controller, nil } -func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *appsv1.DaemonSet) (bool, bool) { - shouldRun, shouldContinueRunning := NodeShouldRunDaemonPod(node, ds) +func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(logger klog.Logger, node *v1.Node, ds *appsv1.DaemonSet) (bool, bool) { + shouldRun, shouldContinueRunning := NodeShouldRunDaemonPod(logger, node, ds) if shouldRun && shouldContinueRunning { if matches, matchErr := dsc.namespaceNodeSelectorMatches(node, ds); !matches || matchErr != nil { return false, false diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index c18e09f54ad03..926c9c9dd43e9 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -179,7 +179,7 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae if err != nil { return fmt.Errorf("couldn't get node for nodeName %q: %v", nodeName, err) } - if shouldRun, _ := dsc.nodeShouldRunDaemonPod(node, ds); !shouldRun { + if shouldRun, _ := dsc.nodeShouldRunDaemonPod(logger, node, ds); !shouldRun { logger.V(5).Info("DaemonSet pod on node is not available and does not match scheduling constraints, remove old pod", "daemonset", klog.KObj(ds), "node", nodeName, "oldPod", klog.KObj(oldPod)) oldPodsToDelete = append(oldPodsToDelete, oldPod.Name) continue @@ -196,7 +196,7 @@ func (dsc *DaemonSetsController) rollingUpdate(ctx context.Context, ds *apps.Dae if err != nil { return fmt.Errorf("couldn't get node for nodeName %q: %v", nodeName, err) } - if shouldRun, _ := dsc.nodeShouldRunDaemonPod(node, ds); !shouldRun { + if shouldRun, _ := dsc.nodeShouldRunDaemonPod(logger, node, ds); !shouldRun { shouldNotRunPodsToDelete = append(shouldNotRunPodsToDelete, oldPod.Name) continue } @@ -586,7 +586,7 @@ func (dsc *DaemonSetsController) updatedDesiredNodeCounts(ctx context.Context, d logger := klog.FromContext(ctx) for i := range nodeList { node := nodeList[i] - wantToRun, _ := dsc.nodeShouldRunDaemonPod(node, ds) + wantToRun, _ := dsc.nodeShouldRunDaemonPod(logger, node, ds) if !wantToRun { continue } diff --git a/pkg/controller/deployment/deployment_controller.go b/pkg/controller/deployment/deployment_controller.go index ec63afd24fbe2..8c5412b868c0d 100644 --- a/pkg/controller/deployment/deployment_controller.go +++ b/pkg/controller/deployment/deployment_controller.go @@ -24,6 +24,7 @@ import ( "context" "fmt" "reflect" + "sync" "time" apps "k8s.io/api/apps/v1" @@ -167,20 +168,25 @@ func (dc *DeploymentController) Run(ctx context.Context, workers int) { dc.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: dc.client.CoreV1().Events("")}) defer dc.eventBroadcaster.Shutdown() - defer dc.queue.ShutDown() - logger := klog.FromContext(ctx) logger.Info("Starting controller", "controller", "deployment") - defer logger.Info("Shutting down controller", "controller", "deployment") - if !cache.WaitForNamedCacheSync("deployment", ctx.Done(), dc.dListerSynced, dc.rsListerSynced, dc.podListerSynced) { + var wg sync.WaitGroup + defer func() { + logger.Info("Shutting down controller", "controller", "deployment") + dc.queue.ShutDown() + wg.Wait() + }() + + if !cache.WaitForNamedCacheSyncWithContext(ctx, dc.dListerSynced, dc.rsListerSynced, dc.podListerSynced) { return } for i := 0; i < workers; i++ { - go wait.UntilWithContext(ctx, dc.worker, time.Second) + wg.Go(func() { + wait.UntilWithContext(ctx, dc.worker, time.Second) + }) } - <-ctx.Done() } diff --git a/pkg/controller/deployment/progress.go b/pkg/controller/deployment/progress.go index 279e201b7063f..f7963a6b59ee6 100644 --- a/pkg/controller/deployment/progress.go +++ b/pkg/controller/deployment/progress.go @@ -152,7 +152,7 @@ func (dc *DeploymentController) getReplicaFailures(allRSs []*apps.ReplicaSet, ne } // used for unit testing -var nowFn = func() time.Time { return time.Now() } +var nowFn = time.Now // requeueStuckDeployment checks whether the provided deployment needs to be synced for a progress // check. It returns the time after the deployment will be requeued for the progress check, 0 if it diff --git a/pkg/controller/deployment/util/deployment_util.go b/pkg/controller/deployment/util/deployment_util.go index 473812a120ded..e49b5a94208ed 100644 --- a/pkg/controller/deployment/util/deployment_util.go +++ b/pkg/controller/deployment/util/deployment_util.go @@ -766,7 +766,7 @@ func DeploymentProgressing(deployment *apps.Deployment, newStatus *apps.Deployme } // used for unit testing -var nowFn = func() time.Time { return time.Now() } +var nowFn = time.Now // DeploymentTimedOut considers a deployment to have timed out once its condition that reports progress // is older than progressDeadlineSeconds or a Progressing condition with a TimedOutReason reason already diff --git a/pkg/controller/devicetainteviction/config/doc.go b/pkg/controller/devicetainteviction/config/doc.go new file mode 100644 index 0000000000000..e605045a40158 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 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. +*/ + +// +k8s:deepcopy-gen=package + +package config diff --git a/pkg/controller/devicetainteviction/config/types.go b/pkg/controller/devicetainteviction/config/types.go new file mode 100644 index 0000000000000..8bc0544ae0ea1 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/types.go @@ -0,0 +1,26 @@ +/* +Copyright 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 config + +// DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller. +type DeviceTaintEvictionControllerConfiguration struct { + // ConcurrentSyncs is the number of operations (deleting a pod, updating a ResourcClaim status, etc.) + // that will be done concurrently. Larger number = processing, but more CPU (and network) load. + // + // The default is 10. + ConcurrentSyncs int32 +} diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/conversion.go b/pkg/controller/devicetainteviction/config/v1alpha1/conversion.go new file mode 100644 index 0000000000000..6b441c434c1ca --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/conversion.go @@ -0,0 +1,40 @@ +/* +Copyright 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 v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/kube-controller-manager/config/v1alpha1" + "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" +) + +// Important! The public back-and-forth conversion functions for the types in this package +// with DeviceTaintEvictionControllerConfiguration types need to be manually exposed like this in order for +// other packages that reference this package to be able to call these conversion functions +// in an autogenerated manner. +// TODO: Fix the bug in conversion-gen so it automatically discovers these Convert_* functions +// in autogenerated code as well. + +// Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration is an autogenerated conversion function. +func Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in *v1alpha1.DeviceTaintEvictionControllerConfiguration, out *config.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error { + return autoConvert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in, out, s) +} + +// Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration is an autogenerated conversion function. +func Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in *config.DeviceTaintEvictionControllerConfiguration, out *v1alpha1.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error { + return autoConvert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in, out, s) +} diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/defaults.go b/pkg/controller/devicetainteviction/config/v1alpha1/defaults.go new file mode 100644 index 0000000000000..e9295e561f733 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/defaults.go @@ -0,0 +1,41 @@ +/* +Copyright 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 v1alpha1 + +import ( + kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1" +) + +// RecommendedDefaultDeviceTaintEvictionControllerConfiguration defaults a pointer to a +// DeviceTaintEvictionControllerConfiguration struct. This will set the recommended default +// values, but they may be subject to change between API versions. This function +// is intentionally not registered in the scheme as a "normal" `SetDefaults_Foo` +// function to allow consumers of this type to set whatever defaults for their +// embedded configs. Forcing consumers to use these defaults would be problematic +// as defaulting in the scheme is done as part of the conversion, and there would +// be no easy way to opt-out. Instead, if you want to use this defaulting method +// run it in your wrapper struct of this type in its `SetDefaults_` method. +func RecommendedDefaultDeviceTaintEvictionControllerConfiguration(obj *kubectrlmgrconfigv1alpha1.DeviceTaintEvictionControllerConfiguration) { + if obj.ConcurrentSyncs == 0 { + // This is a compromise between getting work done and not overwhelming the apiserver + // and pod informers. Integration testing with 100 workers modified pods so quickly + // that a watch in the integration test couldn't keep up: + // cacher.go:855] cacher (pods): 100 objects queued in incoming channel. + // cache_watcher.go:203] Forcing pods watcher close due to unresponsiveness: key: "/pods/", labels: "", fields: "". len(c.input) = 10, len(c.result) = 10, graceful = false + obj.ConcurrentSyncs = 8 + } +} diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/defaults_test.go b/pkg/controller/devicetainteviction/config/v1alpha1/defaults_test.go new file mode 100644 index 0000000000000..7f07f608407af --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/defaults_test.go @@ -0,0 +1,31 @@ +/* +Copyright 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 v1alpha1 + +import ( + "testing" + + kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1" +) + +func TestRecommendedDefaultDeviceTaintEvictionControllerConfiguration(t *testing.T) { + config := new(kubectrlmgrconfigv1alpha1.DeviceTaintEvictionControllerConfiguration) + RecommendedDefaultDeviceTaintEvictionControllerConfiguration(config) + if config.ConcurrentSyncs != 8 { + t.Errorf("incorrect default value, expected 8 but got %v", config.ConcurrentSyncs) + } +} diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/doc.go b/pkg/controller/devicetainteviction/config/v1alpha1/doc.go new file mode 100644 index 0000000000000..67b6807837536 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 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. +*/ + +// +k8s:deepcopy-gen=package +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/controller/devicetainteviction/config +// +k8s:conversion-gen-external-types=k8s.io/kube-controller-manager/config/v1alpha1 + +package v1alpha1 diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/register.go b/pkg/controller/devicetainteviction/config/v1alpha1/register.go new file mode 100644 index 0000000000000..85bbf3effe499 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/register.go @@ -0,0 +1,31 @@ +/* +Copyright 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 v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + // SchemeBuilder is the scheme builder with scheme init functions to run for this API package + SchemeBuilder runtime.SchemeBuilder + // localSchemeBuilder extends the SchemeBuilder instance with the external types. In this package, + // defaulting and conversion init funcs are registered as well. + localSchemeBuilder = &SchemeBuilder + // AddToScheme is a global function that registers this API group & version to a scheme + AddToScheme = localSchemeBuilder.AddToScheme +) diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.conversion.go b/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.conversion.go new file mode 100644 index 0000000000000..ec6654aa75d96 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.conversion.go @@ -0,0 +1,92 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + configv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1" + config "k8s.io/kubernetes/pkg/controller/devicetainteviction/config" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*configv1alpha1.GroupResource)(nil), (*v1.GroupResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_GroupResource_To_v1_GroupResource(a.(*configv1alpha1.GroupResource), b.(*v1.GroupResource), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*v1.GroupResource)(nil), (*configv1alpha1.GroupResource)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_GroupResource_To_v1alpha1_GroupResource(a.(*v1.GroupResource), b.(*configv1alpha1.GroupResource), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*config.DeviceTaintEvictionControllerConfiguration)(nil), (*configv1alpha1.DeviceTaintEvictionControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(a.(*config.DeviceTaintEvictionControllerConfiguration), b.(*configv1alpha1.DeviceTaintEvictionControllerConfiguration), scope) + }); err != nil { + return err + } + if err := s.AddConversionFunc((*configv1alpha1.DeviceTaintEvictionControllerConfiguration)(nil), (*config.DeviceTaintEvictionControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(a.(*configv1alpha1.DeviceTaintEvictionControllerConfiguration), b.(*config.DeviceTaintEvictionControllerConfiguration), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in *configv1alpha1.DeviceTaintEvictionControllerConfiguration, out *config.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error { + out.ConcurrentSyncs = in.ConcurrentSyncs + return nil +} + +func autoConvert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in *config.DeviceTaintEvictionControllerConfiguration, out *configv1alpha1.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error { + out.ConcurrentSyncs = in.ConcurrentSyncs + return nil +} + +func autoConvert_v1alpha1_GroupResource_To_v1_GroupResource(in *configv1alpha1.GroupResource, out *v1.GroupResource, s conversion.Scope) error { + out.Group = in.Group + out.Resource = in.Resource + return nil +} + +// Convert_v1alpha1_GroupResource_To_v1_GroupResource is an autogenerated conversion function. +func Convert_v1alpha1_GroupResource_To_v1_GroupResource(in *configv1alpha1.GroupResource, out *v1.GroupResource, s conversion.Scope) error { + return autoConvert_v1alpha1_GroupResource_To_v1_GroupResource(in, out, s) +} + +func autoConvert_v1_GroupResource_To_v1alpha1_GroupResource(in *v1.GroupResource, out *configv1alpha1.GroupResource, s conversion.Scope) error { + out.Group = in.Group + out.Resource = in.Resource + return nil +} + +// Convert_v1_GroupResource_To_v1alpha1_GroupResource is an autogenerated conversion function. +func Convert_v1_GroupResource_To_v1alpha1_GroupResource(in *v1.GroupResource, out *configv1alpha1.GroupResource, s conversion.Scope) error { + return autoConvert_v1_GroupResource_To_v1alpha1_GroupResource(in, out, s) +} diff --git a/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.deepcopy.go b/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000000000..61f6555edfc5a --- /dev/null +++ b/pkg/controller/devicetainteviction/config/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,22 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 diff --git a/pkg/controller/devicetainteviction/config/zz_generated.deepcopy.go b/pkg/controller/devicetainteviction/config/zz_generated.deepcopy.go new file mode 100644 index 0000000000000..a42fadcd4a353 --- /dev/null +++ b/pkg/controller/devicetainteviction/config/zz_generated.deepcopy.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package config + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeviceTaintEvictionControllerConfiguration) DeepCopyInto(out *DeviceTaintEvictionControllerConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceTaintEvictionControllerConfiguration. +func (in *DeviceTaintEvictionControllerConfiguration) DeepCopy() *DeviceTaintEvictionControllerConfiguration { + if in == nil { + return nil + } + out := new(DeviceTaintEvictionControllerConfiguration) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/devicetainteviction/device_taint_eviction.go b/pkg/controller/devicetainteviction/device_taint_eviction.go index d3198e66f1f38..d6075e98b3157 100644 --- a/pkg/controller/devicetainteviction/device_taint_eviction.go +++ b/pkg/controller/devicetainteviction/device_taint_eviction.go @@ -18,8 +18,9 @@ package devicetainteviction import ( "context" - "errors" "fmt" + "iter" + "maps" "math" "slices" "strings" @@ -29,14 +30,18 @@ import ( v1 "k8s.io/api/core/v1" resourceapi "k8s.io/api/resource/v1" + resourcealpha "k8s.io/api/resource/v1alpha3" apiequality "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/diff" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/sets" utilfeature "k8s.io/apiserver/pkg/util/feature" + metav1ac "k8s.io/client-go/applyconfigurations/meta/v1" + resourceac "k8s.io/client-go/applyconfigurations/resource/v1alpha3" coreinformers "k8s.io/client-go/informers/core/v1" resourceinformers "k8s.io/client-go/informers/resource/v1" resourcealphainformers "k8s.io/client-go/informers/resource/v1alpha3" @@ -44,10 +49,11 @@ import ( "k8s.io/client-go/kubernetes/scheme" v1core "k8s.io/client-go/kubernetes/typed/core/v1" corelisters "k8s.io/client-go/listers/core/v1" + resourcealphalisters "k8s.io/client-go/listers/resource/v1alpha3" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" "k8s.io/dynamic-resource-allocation/resourceclaim" - resourceslicetracker "k8s.io/dynamic-resource-allocation/resourceslice/tracker" "k8s.io/klog/v2" apipod "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/controller/devicetainteviction/metrics" @@ -57,9 +63,10 @@ import ( ) const ( - // retries is the number of times that the controller tries to delete a pod - // that needs to be evicted. - retries = 5 + // ruleStatusPeriod is the shortest time between DeviceTaintRule status + // updates while eviction is in progress. Once it is done, it no longer gets + // updated until in progress again. + ruleStatusPeriod = 10 * time.Second ) // Controller listens to Taint changes of DRA devices and Toleration changes of ResourceClaims, @@ -90,18 +97,29 @@ type Controller struct { podLister corelisters.PodLister claimInformer resourceinformers.ResourceClaimInformer sliceInformer resourceinformers.ResourceSliceInformer - taintInformer resourcealphainformers.DeviceTaintRuleInformer + ruleInformer resourcealphainformers.DeviceTaintRuleInformer classInformer resourceinformers.DeviceClassInformer + ruleLister resourcealphalisters.DeviceTaintRuleLister haveSynced []cache.InformerSynced + hasSynced atomic.Int32 metrics metrics.Metrics + workqueue workqueue.TypedRateLimitingInterface[workItem] - // evictPod ensures that the pod gets evicted at the specified time. - // It doesn't block. - evictPod func(pod tainteviction.NamespacedObject, fireAt time.Time) + evictPodHook func(pod tainteviction.NamespacedObject, eviction evictionAndReason) + cancelEvictHook func(pod tainteviction.NamespacedObject) bool - // cancelEvict cancels eviction set up with evictPod earlier. - // Idempotent, returns false if there was nothing to cancel. - cancelEvict func(pod tainteviction.NamespacedObject) bool + // mutex protects the following shared data structures. + mutex sync.Mutex + + // deletePodAt maps a pod to the time when it is meant to be evicted. + // + // The entry for pod gets deleted when eviction is no longer necessary + // and updated when the time changes. + deletePodAt map[tainteviction.NamespacedObject]evictionAndReason + + // maybeDeletePodCount counts how often a worker checked a pod. + // This is useful for unit testing, but probably not a good public metric. + maybeDeletePodCount int64 // allocatedClaims holds all currently known allocated claims. allocatedClaims map[types.NamespacedName]allocatedClaim // A value is slightly more efficient in BenchmarkTaintUntaint (less allocations!). @@ -109,7 +127,19 @@ type Controller struct { // pools indexes all slices by driver and pool name. pools map[poolID]pool - hasSynced atomic.Int32 + // evictingRules tracks all DeviceTaintRules by name which cause pod eviction. + evictingRules map[string]*resourcealpha.DeviceTaintRule + + // taintRuleStats tracks information about work that was done for a specific DeviceTaintRule instance. + // + // This is potentially a different set of rules than in evictingRules because a rule which is no + // longer evicting might have evicted in the past and thus can have some relevant stats. + taintRuleStats map[types.UID]taintRuleStats +} + +type taintRuleStats struct { + // numEvictedPods is the number of pods evicted because of this rule since starting the controller. + numEvictedPods int64 } type poolID struct { @@ -117,7 +147,8 @@ type poolID struct { } type pool struct { - slices sets.Set[*resourceapi.ResourceSlice] + // slices maps the global name to the current instance under that name. + slices map[string]*resourceapi.ResourceSlice maxGeneration int64 } @@ -127,10 +158,10 @@ func (p *pool) addSlice(slice *resourceapi.ResourceSlice) { return } if p.slices == nil { - p.slices = sets.New[*resourceapi.ResourceSlice]() + p.slices = make(map[string]*resourceapi.ResourceSlice) p.maxGeneration = math.MinInt64 } - p.slices.Insert(slice) + p.slices[slice.Name] = slice // Adding a slice can only increase the generation. if slice.Spec.Pool.Generation > p.maxGeneration { @@ -143,13 +174,13 @@ func (p *pool) removeSlice(slice *resourceapi.ResourceSlice) { if slice == nil { return } - p.slices.Delete(slice) + delete(p.slices, slice.Name) // Removing a slice might have decreased the generation to // that of some other slice. if slice.Spec.Pool.Generation == p.maxGeneration { maxGeneration := int64(math.MinInt64) - for slice := range p.slices { + for _, slice := range p.slices { if slice.Spec.Pool.Generation > maxGeneration { maxGeneration = slice.Spec.Pool.Generation } @@ -162,7 +193,7 @@ func (p *pool) removeSlice(slice *resourceapi.ResourceSlice) { // The result is sorted by device name. func (p pool) getTaintedDevices() []taintedDevice { var buffer []taintedDevice - for slice := range p.slices { + for _, slice := range p.slices { if slice.Spec.Pool.Generation != p.maxGeneration { continue } @@ -184,19 +215,19 @@ func (p pool) getTaintedDevices() []taintedDevice { } // getDevice looks up one device by name. Out-dated slices are ignored. -func (p pool) getDevice(deviceName string) *resourceapi.Device { - for slice := range p.slices { +func (p pool) getDevice(deviceName string) (*resourceapi.ResourceSlice, *resourceapi.Device) { + for _, slice := range p.slices { if slice.Spec.Pool.Generation != p.maxGeneration { continue } for i := range slice.Spec.Devices { if slice.Spec.Devices[i].Name == deviceName { - return &slice.Spec.Devices[i] + return slice, &slice.Spec.Devices[i] } } } - return nil + return nil, nil } type taintedDevice struct { @@ -209,38 +240,209 @@ type taintedDevice struct { type allocatedClaim struct { *resourceapi.ResourceClaim - // evictionTime, if non-nil, is the time at which pods using this claim need to be evicted. + // eviction, if non-nil, is the time at which pods using this claim need to be evicted. // This is the smallest value of all such per-device values. // For each device, the value is calculated as `