Compare commits

..

1 Commits

Author SHA1 Message Date
Kubernetes Publisher 37c6d90a1c Update dependencies to v0.34.0-beta.0 tag 2025-07-16 17:58:10 +00:00
33 changed files with 297 additions and 918 deletions

View File

@ -1,8 +1,3 @@
> ⚠️ **This is an automatically published [staged repository](https://git.k8s.io/kubernetes/staging#external-repository-staging-area) for Kubernetes**.
> Pull requests, should be made to the main Kubernetes repository: [https://github.com/kubernetes/kubernetes](https://github.com/kubernetes/kubernetes).
> This repository is read-only for importing, and not used for direct contributions.
> See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
# Kubectl
![kubectl logo](./images/kubectl-logo-medium.png)

46
go.mod
View File

@ -27,24 +27,24 @@ require (
github.com/spf13/pflag v1.0.6
github.com/stretchr/testify v1.10.0
go.yaml.in/yaml/v2 v2.4.2
golang.org/x/sys v0.33.0
gopkg.in/evanphx/json-patch.v4 v4.13.0
k8s.io/api v0.0.0-20250830163657-b903cd06836a
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc
golang.org/x/sys v0.31.0
gopkg.in/evanphx/json-patch.v4 v4.12.0
k8s.io/api v0.34.0-beta.0
k8s.io/apimachinery v0.34.0-beta.0
k8s.io/cli-runtime v0.34.0-beta.0
k8s.io/client-go v0.34.0-beta.0
k8s.io/component-base v0.34.0-beta.0
k8s.io/component-helpers v0.34.0-beta.0
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd
k8s.io/metrics v0.34.0-beta.0
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1
sigs.k8s.io/kustomize/kyaml v0.20.1
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0
sigs.k8s.io/kustomize/kyaml v0.19.0
sigs.k8s.io/randfill v1.0.0
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
sigs.k8s.io/yaml v1.6.0
sigs.k8s.io/structured-merge-diff/v6 v6.2.0
sigs.k8s.io/yaml v1.5.0
)
require (
@ -52,15 +52,16 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/logr v1.4.2 // indirect
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/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
@ -76,19 +77,20 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.26.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/kustomize/api v0.20.1 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
)

92
go.sum
View File

@ -23,12 +23,12 @@ github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
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/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
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/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
@ -50,6 +50,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
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/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
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=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
@ -105,6 +107,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
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=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@ -151,27 +155,27 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
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/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
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/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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
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=
@ -184,49 +188,49 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
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=
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.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.0.0-20250830163657-b903cd06836a h1:qS+abmAu2zbGFkbN1vA7LKS07jsXBN1BvTFXFvaGOLI=
k8s.io/api v0.0.0-20250830163657-b903cd06836a/go.mod h1:/IpJMZ4ur2JBuX+kkBc115bnq09sFfUnbuFNrdEe5yc=
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4 h1:ObQoOWhkcPbMnU7PIHT2pkO2wK66CcBn6vD+77CidHM=
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4/go.mod h1:fDax9lidUgmNSmBlzUrSISURQmHpeyamBbKX9jGbJ3g=
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e h1:dovWIA2PM0UrJrZdUBV8uy5pExliSBXSFeL0bI6IX6E=
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e/go.mod h1:/yp9r2rD6AV7MYM/gmb55/6LttRuURzjhgqbfiFQ0Rg=
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97 h1:6ks7Y8CNm05xZ6eyE0db5IDP54PIyRM3aZhpflG55hI=
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97/go.mod h1:xTpANYjBhGsmpO7Gdw8kMt3yQfciVwyRhbqcq77qwyI=
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7 h1:JQ/dO+EIXNQ+y3Vlez6PC6r7T+0JvNQWrBx6CD3jKls=
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7/go.mod h1:ZZMk2BFRSF/kI9Y5qKmvTk4SJM654XsQ5eJ9cP7mrhw=
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc h1:hEo6RVciD0e0QvtMBgwG9a7fFWb/vkx0Jvw/iQ5i+lQ=
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc/go.mod h1:TmnJ20kJrkgbEqHgHoUxoTksgIUJNCWtv/QM+yBqCF0=
k8s.io/api v0.34.0-beta.0 h1:Yok13a6+0GTJYv56+lSNlgRjF46TLUVv+gaijkLdkB4=
k8s.io/api v0.34.0-beta.0/go.mod h1:OOkMaR1spg7vtjCCQSdxgiRD0Bbp2pcTCBxJtEEb4Pk=
k8s.io/apimachinery v0.34.0-beta.0 h1:C6teSJBCx6ArW7122MM9hQqeGW2w/QQ0lB4x4Z4Iftc=
k8s.io/apimachinery v0.34.0-beta.0/go.mod h1:TP8uyOuDEOnzGpLOdffo8hPnIjVDljZCxCM/fruV+5M=
k8s.io/cli-runtime v0.34.0-beta.0 h1:NFo0hFz5NT1eYLrjFVfG8EoUjif/CFERkFEhoxmZ6Jg=
k8s.io/cli-runtime v0.34.0-beta.0/go.mod h1:z7rkyyZ/kjQb8Ua9FwcCm30NyHK8MIMFNRhRVezAQNE=
k8s.io/client-go v0.34.0-beta.0 h1:snyuzY74xHIp7qxMYul8QRn1WxPNhs281srSl2CX9nY=
k8s.io/client-go v0.34.0-beta.0/go.mod h1:KNxQTmxlWq7adFeedQLu+qVkIBTZqhghesFk41YWyBQ=
k8s.io/component-base v0.34.0-beta.0 h1:w2zC7n8lXbOqsC8UQ97JqTneDsy+ThFcbxQnfQ98/TM=
k8s.io/component-base v0.34.0-beta.0/go.mod h1:KRfhQL9KZm3sywusY38Z2An7ulFDCaL+1juNF4NLhUw=
k8s.io/component-helpers v0.34.0-beta.0 h1:VtrSd4nRzYbHSr6hryNvIojiDOjf5MbjAWjyjGBjFjY=
k8s.io/component-helpers v0.34.0-beta.0/go.mod h1:H+zgzhEnCsKoOzb9PwRw5Ef1cv+cesDucZdjllYeVeM=
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/metrics v0.0.0-20250830171643-3daf20f585bd h1:GNGeQ8o/xw8TwhavcXmWrGKHd0ez5TRx8qRqyncbFH4=
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd/go.mod h1:OYYW2zJf2TAQHmxQwQgSsP/i40dCZig4RQkw3o3vFPE=
k8s.io/metrics v0.34.0-beta.0 h1:vMZWGJ6giJoBK/XAv9HyiWpxtfN70mYZhrlOE3q1Xro=
k8s.io/metrics v0.34.0-beta.0/go.mod h1:3US3VDB32Y9UmMFIVeeODRFI65m8HOCbLPe6t35OuwI=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
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/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 h1:sYJsarwy/SDJfjjLMUqwFDGPwzUtMOQ1i1Ed49+XSbw=
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU=
sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=
sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0 h1:MWtRRDWCwQEeW2rnJTqJMuV6Agy56P53SkbVoJpN7wA=
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0/go.mod h1:XuuZiQF7WdcvZzEYyNww9A0p3LazCKeJmCjeycN8e1I=
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
sigs.k8s.io/structured-merge-diff/v6 v6.2.0 h1:msyqjP8Nyd5sF3QSmJouFSzcBIdwq4ct8d1/7VSBHIQ=
sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=

View File

@ -23,11 +23,10 @@ import (
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
@ -62,10 +61,12 @@ var (
// APIResourceOptions is the start of the data required to perform the operation.
// As new fields are added, add them here instead of referencing the cmd.Flags()
type APIResourceOptions struct {
Output string
SortBy string
APIGroup string
Namespaced bool
Verbs []string
NoHeaders bool
Cached bool
Categories []string
@ -75,8 +76,13 @@ type APIResourceOptions struct {
discoveryClient discovery.CachedDiscoveryInterface
genericiooptions.IOStreams
PrintFlags *PrintFlags
PrintObj printers.ResourcePrinterFunc
}
// groupResource contains the APIGroup and APIResource
type groupResource struct {
APIGroup string
APIGroupVersion string
APIResource metav1.APIResource
}
// NewAPIResourceOptions creates the options for APIResource
@ -84,7 +90,6 @@ func NewAPIResourceOptions(ioStreams genericiooptions.IOStreams) *APIResourceOpt
return &APIResourceOptions{
IOStreams: ioStreams,
Namespaced: true,
PrintFlags: NewPrintFlags(),
}
}
@ -104,7 +109,8 @@ func NewCmdAPIResources(restClientGetter genericclioptions.RESTClientGetter, ioS
},
}
o.PrintFlags.AddFlags(cmd)
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, `Output format. One of: (wide, name).`)
cmd.Flags().StringVar(&o.APIGroup, "api-group", o.APIGroup, "Limit to resources in the specified API group.")
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", o.Namespaced, "If false, non-namespaced resources will be returned, otherwise returning namespaced resources by default.")
@ -117,6 +123,10 @@ func NewCmdAPIResources(restClientGetter genericclioptions.RESTClientGetter, ioS
// Validate checks to the APIResourceOptions to see if there is sufficient information run the command
func (o *APIResourceOptions) Validate() error {
supportedOutputTypes := sets.New[string]("", "wide", "name")
if !supportedOutputTypes.Has(o.Output) {
return fmt.Errorf("--output %v is not available", o.Output)
}
supportedSortTypes := sets.New[string]("", "name", "kind")
if len(o.SortBy) > 0 {
if !supportedSortTypes.Has(o.SortBy) {
@ -141,28 +151,6 @@ func (o *APIResourceOptions) Complete(restClientGetter genericclioptions.RESTCli
o.groupChanged = cmd.Flags().Changed("api-group")
o.nsChanged = cmd.Flags().Changed("namespaced")
var printer printers.ResourcePrinter
if o.PrintFlags.OutputFormat != nil {
printer, err = o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(object runtime.Object, out io.Writer) error {
errs := []error{}
if !*o.PrintFlags.NoHeaders &&
(o.PrintFlags.OutputFormat == nil || *o.PrintFlags.OutputFormat == "" || *o.PrintFlags.OutputFormat == "wide") {
if err = printContextHeaders(out, *o.PrintFlags.OutputFormat); err != nil {
errs = append(errs, err)
}
}
if err := printer.PrintObj(object, out); err != nil {
errs = append(errs, err)
}
return utilerrors.NewAggregate(errs)
}
}
return nil
}
@ -182,7 +170,7 @@ func (o *APIResourceOptions) RunAPIResources() error {
errs = append(errs, err)
}
var allResources []*metav1.APIResourceList
resources := []groupResource{}
for _, list := range lists {
if len(list.APIResources) == 0 {
@ -192,14 +180,6 @@ func (o *APIResourceOptions) RunAPIResources() error {
if err != nil {
continue
}
apiList := &metav1.APIResourceList{
TypeMeta: metav1.TypeMeta{
Kind: "APIResourceList",
APIVersion: "v1",
},
GroupVersion: gv.String(),
}
var apiResources []metav1.APIResource
for _, resource := range list.APIResources {
if len(resource.Verbs) == 0 {
continue
@ -220,32 +200,58 @@ func (o *APIResourceOptions) RunAPIResources() error {
if len(o.Categories) > 0 && !sets.New[string](resource.Categories...).HasAll(o.Categories...) {
continue
}
// set these because we display a concatenation of these two values under APIVERSION column of human-readable output
resource.Group = gv.Group
resource.Version = gv.Version
apiResources = append(apiResources, resource)
resources = append(resources, groupResource{
APIGroup: gv.Group,
APIGroupVersion: gv.String(),
APIResource: resource,
})
}
apiList.APIResources = apiResources
allResources = append(allResources, apiList)
}
flatList := &metav1.APIResourceList{
TypeMeta: metav1.TypeMeta{
APIVersion: allResources[0].APIVersion,
Kind: allResources[0].Kind,
},
}
for _, resource := range allResources {
flatList.APIResources = append(flatList.APIResources, resource.APIResources...)
if o.NoHeaders == false && o.Output != "name" {
if err = printContextHeaders(w, o.Output); err != nil {
return err
}
}
sort.Stable(sortableResource{flatList.APIResources, o.SortBy})
err = o.PrintObj(flatList, w)
if err != nil {
errs = append(errs, err)
sort.Stable(sortableResource{resources, o.SortBy})
for _, r := range resources {
switch o.Output {
case "name":
name := r.APIResource.Name
if len(r.APIGroup) > 0 {
name += "." + r.APIGroup
}
if _, err := fmt.Fprintf(w, "%s\n", name); err != nil {
errs = append(errs, err)
}
case "wide":
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\t%v\t%v\n",
r.APIResource.Name,
strings.Join(r.APIResource.ShortNames, ","),
r.APIGroupVersion,
r.APIResource.Namespaced,
r.APIResource.Kind,
strings.Join(r.APIResource.Verbs, ","),
strings.Join(r.APIResource.Categories, ",")); err != nil {
errs = append(errs, err)
}
case "":
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n",
r.APIResource.Name,
strings.Join(r.APIResource.ShortNames, ","),
r.APIGroupVersion,
r.APIResource.Namespaced,
r.APIResource.Kind); err != nil {
errs = append(errs, err)
}
}
}
return utilerrors.NewAggregate(errs)
if len(errs) > 0 {
return errors.NewAggregate(errs)
}
return nil
}
func printContextHeaders(out io.Writer, output string) error {
@ -258,7 +264,7 @@ func printContextHeaders(out io.Writer, output string) error {
}
type sortableResource struct {
resources []metav1.APIResource
resources []groupResource
sortBy string
}
@ -271,7 +277,7 @@ func (s sortableResource) Less(i, j int) bool {
if ret > 0 {
return false
} else if ret == 0 {
return strings.Compare(s.resources[i].Name, s.resources[j].Name) < 0
return strings.Compare(s.resources[i].APIResource.Name, s.resources[j].APIResource.Name) < 0
}
return true
}
@ -279,9 +285,9 @@ func (s sortableResource) Less(i, j int) bool {
func (s sortableResource) compareValues(i, j int) (string, string) {
switch s.sortBy {
case "name":
return s.resources[i].Name, s.resources[j].Name
return s.resources[i].APIResource.Name, s.resources[j].APIResource.Name
case "kind":
return s.resources[i].Kind, s.resources[j].Kind
return s.resources[i].APIResource.Kind, s.resources[j].APIResource.Kind
}
return s.resources[i].Group, s.resources[j].Group
return s.resources[i].APIGroup, s.resources[j].APIGroup
}

View File

@ -17,17 +17,13 @@ limitations under the License.
package apiresources
import (
"encoding/json"
"strings"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
apiequality "k8s.io/apimachinery/pkg/api/equality"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericiooptions"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
"sigs.k8s.io/yaml"
)
func TestAPIResourcesComplete(t *testing.T) {
@ -52,16 +48,6 @@ See 'kubectl api-resources -h' for help and examples`
if err.Error() != expectedError {
t.Fatalf("Unexpected error: %v\n expected: %v", err, expectedError)
}
*o.PrintFlags.OutputFormat = "foo"
err = o.Complete(tf, cmd, []string{})
if err == nil {
t.Fatalf("An error was expected but not returned")
}
expectedError = `unable to match a printer suitable for the output format "foo", allowed formats are:`
if !strings.HasPrefix(err.Error(), expectedError) {
t.Fatalf("Unexpected error: %v\n expected: %v", err, expectedError)
}
}
func TestAPIResourcesValidate(t *testing.T) {
@ -75,6 +61,13 @@ func TestAPIResourcesValidate(t *testing.T) {
optionSetupFn: func(o *APIResourceOptions) {},
expectedError: "",
},
{
name: "invalid output",
optionSetupFn: func(o *APIResourceOptions) {
o.Output = "foo"
},
expectedError: "--output foo is not available",
},
{
name: "invalid sort by",
optionSetupFn: func(o *APIResourceOptions) {
@ -329,92 +322,3 @@ bazzes b somegroup/v1 true Baz
})
}
}
// TestAPIResourcesRunJsonYaml is doing same thing as TestAPIResourcesRun but for JSON and YAML outputs
// A separate test function is created because we are using apieqaulity.Semantic.DeepEqual
// to check equality between input and output
func TestAPIResourcesRunJsonYaml(t *testing.T) {
dc := cmdtesting.NewFakeCachedDiscoveryClient()
tf := cmdtesting.NewTestFactory().WithDiscoveryClient(dc)
defer tf.Cleanup()
testCases := []struct {
name string
expectedInvalidations int
preferredResources []*v1.APIResourceList
}{
{
name: "one",
preferredResources: []*v1.APIResourceList{
{
GroupVersion: "v1",
APIResources: []v1.APIResource{
{
Name: "foos",
Namespaced: false,
Kind: "Foo",
Verbs: []string{"get", "list"},
ShortNames: []string{"f", "fo"},
Categories: []string{"some-category"},
},
},
},
},
},
{
name: "two",
preferredResources: []*v1.APIResourceList{
{
GroupVersion: "somegroup/v1",
APIResources: []v1.APIResource{
{
Name: "bazzes",
Namespaced: true,
Kind: "Baz",
Verbs: []string{"get", "list", "create", "delete"},
ShortNames: []string{"b"},
Categories: []string{"some-category", "another-category"},
},
},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(tt *testing.T) {
dc.PreferredResources = tc.preferredResources
ioStreams, _, out, errOut := genericiooptions.NewTestIOStreams()
for _, v := range []string{"json", "yaml"} {
cmd := NewCmdAPIResources(tf, ioStreams)
err := cmd.Flags().Set("output", v)
require.NoError(tt, err)
cmd.Run(cmd, []string{})
if errOut.Len() > 0 {
t.Fatalf("unexpected error output: %s", errOut.String())
}
apiResourceList := v1.APIResourceList{}
switch v {
case "json":
err = json.Unmarshal(out.Bytes(), &apiResourceList)
case "yaml":
err = yaml.Unmarshal(out.Bytes(), &apiResourceList)
}
require.NoError(tt, err)
// this will undo custom value we add in RunAPIResources in the lines:
// resource.Group = gv.Group
// resource.Version = gv.Version
apiResourceList.GroupVersion = apiResourceList.APIResources[0].Group + "/" + apiResourceList.APIResources[0].Version
apiResourceList.APIResources[0].Version = ""
apiResourceList.APIResources[0].Group = ""
if !apiequality.Semantic.DeepEqual(tc.preferredResources[0].APIResources[0], apiResourceList.APIResources[0]) {
tt.Fatalf("expected output: [%v]\n, but got [%v]", tc.preferredResources[0].APIResources[0], apiResourceList.APIResources[0])
}
}
})
}
}

View File

@ -1,233 +0,0 @@
/*
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 apiresources
import (
"fmt"
"io"
"strings"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
)
type PrintFlags struct {
JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
NamePrintFlags NamePrintFlags
HumanReadableFlags HumanPrintFlags
NoHeaders *bool
OutputFormat *string
}
func NewPrintFlags() *PrintFlags {
outputFormat := ""
noHeaders := false
return &PrintFlags{
OutputFormat: &outputFormat,
NoHeaders: &noHeaders,
JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(),
NamePrintFlags: APIResourcesNewNamePrintFlags(),
HumanReadableFlags: APIResourcesHumanReadableFlags(),
}
}
func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
f.JSONYamlPrintFlags.AddFlags(cmd)
f.HumanReadableFlags.AddFlags(cmd)
f.NamePrintFlags.AddFlags(cmd)
if f.OutputFormat != nil {
cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf("Output format. One of: (%s).", strings.Join(f.AllowedFormats(), ", ")))
}
if f.NoHeaders != nil {
cmd.Flags().BoolVar(f.NoHeaders, "no-headers", *f.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
}
}
// PrintOptions struct defines a struct for various print options
type PrintOptions struct {
SortBy *string
NoHeaders bool
Wide bool
}
type HumanPrintFlags struct {
SortBy *string
NoHeaders bool
}
func (f *HumanPrintFlags) AllowedFormats() []string {
return []string{"wide"}
}
// AddFlags receives a *cobra.Command reference and binds
// flags related to human-readable printing to it
func (f *HumanPrintFlags) AddFlags(c *cobra.Command) {
if f.SortBy != nil {
c.Flags().StringVar(f.SortBy, "sort-by", *f.SortBy, "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
}
}
// ToPrinter receives an outputFormat and returns a printer capable of
// handling human-readable output.
func (f *HumanPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
if len(outputFormat) > 0 && outputFormat != "wide" {
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, AllowedFormats: f.AllowedFormats()}
}
p := HumanReadablePrinter{
options: PrintOptions{
NoHeaders: f.NoHeaders,
Wide: outputFormat == "wide",
},
}
return p, nil
}
type HumanReadablePrinter struct {
options PrintOptions
}
func (f HumanReadablePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
flatList, ok := obj.(*metav1.APIResourceList)
if !ok {
return fmt.Errorf("object is not a APIResourceList")
}
var errs []error
for _, r := range flatList.APIResources {
gv, err := schema.ParseGroupVersion(strings.Join([]string{r.Group, r.Version}, "/"))
if err != nil {
errs = append(errs, err)
continue
}
if f.options.Wide {
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\t%v\t%v\n",
r.Name,
strings.Join(r.ShortNames, ","),
gv.String(),
r.Namespaced,
r.Kind,
strings.Join(r.Verbs, ","),
strings.Join(r.Categories, ",")); err != nil {
errs = append(errs, err)
}
continue
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n",
r.Name,
strings.Join(r.ShortNames, ","),
gv.String(),
r.Namespaced,
r.Kind); err != nil {
errs = append(errs, err)
}
}
return utilerrors.NewAggregate(errs)
}
type NamePrintFlags struct{}
func APIResourcesNewNamePrintFlags() NamePrintFlags {
return NamePrintFlags{}
}
func (f *NamePrintFlags) AllowedFormats() []string {
return []string{"name"}
}
// AddFlags receives a *cobra.Command reference and binds
// flags related to name printing to it
func (f *NamePrintFlags) AddFlags(_ *cobra.Command) {}
// ToPrinter receives an outputFormat and returns a printer capable of
// handling human-readable output.
func (f *NamePrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
if outputFormat == "name" {
return NamePrinter{}, nil
}
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, AllowedFormats: f.AllowedFormats()}
}
type NamePrinter struct{}
func (f NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
flatList, ok := obj.(*metav1.APIResourceList)
if !ok {
return fmt.Errorf("object is not a APIResourceList")
}
var errs []error
for _, r := range flatList.APIResources {
name := r.Name
if len(r.Group) > 0 {
name += "." + r.Group
}
if _, err := fmt.Fprintf(w, "%s\n", name); err != nil {
errs = append(errs, err)
}
}
return utilerrors.NewAggregate(errs)
}
func APIResourcesHumanReadableFlags() HumanPrintFlags {
return HumanPrintFlags{
SortBy: nil,
NoHeaders: false,
}
}
func (f *PrintFlags) AllowedFormats() []string {
ret := []string{}
ret = append(ret, f.JSONYamlPrintFlags.AllowedFormats()...)
ret = append(ret, f.NamePrintFlags.AllowedFormats()...)
ret = append(ret, f.HumanReadableFlags.AllowedFormats()...)
return ret
}
func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
outputFormat := ""
if f.OutputFormat != nil {
outputFormat = *f.OutputFormat
}
noHeaders := false
if f.NoHeaders != nil {
noHeaders = *f.NoHeaders
}
f.HumanReadableFlags.NoHeaders = noHeaders
if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.HumanReadableFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
return p, err
}
return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()}
}

View File

@ -42,7 +42,6 @@ import (
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"k8s.io/kubectl/pkg/util/term"
)
var (
@ -301,9 +300,7 @@ func (o *AttachOptions) Run() error {
sizePlusOne.Height++
// this call spawns a goroutine to monitor/update the terminal size
sizeQueue = &terminalSizeQueueAdapter{
delegate: t.MonitorSize(&sizePlusOne, size),
}
sizeQueue = t.MonitorSize(&sizePlusOne, size)
}
o.DisableStderr = true
@ -360,18 +357,3 @@ func (o *AttachOptions) reattachMessage(containerName string, rawTTY bool) strin
}
return fmt.Sprintf("Session ended, resume using '%s %s -c %s -i -t' command when the pod is running", o.CommandName, o.Pod.Name, containerName)
}
type terminalSizeQueueAdapter struct {
delegate term.TerminalSizeQueue
}
func (a *terminalSizeQueueAdapter) Next() *remotecommand.TerminalSize {
next := a.delegate.Next()
if next == nil {
return nil
}
return &remotecommand.TerminalSize{
Width: next.Width,
Height: next.Height,
}
}

View File

@ -22,7 +22,6 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
rbacv1 "k8s.io/api/rbac/v1"
@ -34,7 +33,6 @@ import (
"k8s.io/cli-runtime/pkg/resource"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
"k8s.io/client-go/util/retry"
"k8s.io/component-helpers/auth/rbac/reconciliation"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
@ -199,9 +197,6 @@ func (o *ReconcileOptions) Validate() error {
// RunReconcile performs the execution
func (o *ReconcileOptions) RunReconcile() error {
// conflictBackoff retries up to 3 times on conflict, with no delay
conflictBackoff := wait.Backoff{Steps: 3}
return o.Visitor.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
@ -218,14 +213,7 @@ func (o *ReconcileOptions) RunReconcile() error {
Client: o.RBACClient,
},
}
var (
result *reconciliation.ReconcileClusterRoleResult
err error
)
retry.RetryOnConflict(conflictBackoff, func() error {
result, err = reconcileOptions.Run()
return err
})
result, err := reconcileOptions.Run()
if err != nil {
return err
}
@ -240,14 +228,7 @@ func (o *ReconcileOptions) RunReconcile() error {
Client: o.RBACClient.ClusterRoles(),
},
}
var (
result *reconciliation.ReconcileClusterRoleResult
err error
)
retry.RetryOnConflict(conflictBackoff, func() error {
result, err = reconcileOptions.Run()
return err
})
result, err := reconcileOptions.Run()
if err != nil {
return err
}
@ -263,14 +244,10 @@ func (o *ReconcileOptions) RunReconcile() error {
NamespaceClient: o.NamespaceClient.Namespaces(),
},
}
var (
result *reconciliation.ReconcileClusterRoleBindingResult
err error
)
retry.RetryOnConflict(conflictBackoff, func() error {
result, err = reconcileOptions.Run()
result, err := reconcileOptions.Run()
if err != nil {
return err
})
}
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
case *rbacv1.ClusterRoleBinding:
@ -282,14 +259,10 @@ func (o *ReconcileOptions) RunReconcile() error {
Client: o.RBACClient.ClusterRoleBindings(),
},
}
var (
result *reconciliation.ReconcileClusterRoleBindingResult
err error
)
retry.RetryOnConflict(conflictBackoff, func() error {
result, err = reconcileOptions.Run()
result, err := reconcileOptions.Run()
if err != nil {
return err
})
}
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
case *rbacv1beta1.Role,

View File

@ -129,8 +129,7 @@ func NewCmdConfigSetCredentials(out io.Writer, configAccess clientcmd.ConfigAcce
}
// NewCmdConfigSetAuthInfo returns a Command instance for 'config set-credentials' sub command
//
// Deprecated: Use NewCmdConfigSetCredentials instead
// DEPRECATED: Use NewCmdConfigSetCredentials instead
func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
return NewCmdConfigSetCredentials(out, configAccess)
}

View File

@ -106,7 +106,7 @@ func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *c
cmdutil.AddValidateFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only CronJob is supported).")
cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
return cmd
}

View File

@ -19,7 +19,6 @@ package drain
import (
"errors"
"fmt"
"time"
"github.com/spf13/cobra"
@ -152,11 +151,10 @@ func NewDrainCmdOptions(f cmdutil.Factory, ioStreams genericiooptions.IOStreams)
PrintFlags: genericclioptions.NewPrintFlags("drained").WithTypeSetter(scheme.Scheme),
IOStreams: ioStreams,
drainer: &drain.Helper{
GracePeriodSeconds: -1,
EvictErrorRetryDelay: 5 * time.Second,
Out: ioStreams.Out,
ErrOut: ioStreams.ErrOut,
ChunkSize: cmdutil.DefaultChunkSize,
GracePeriodSeconds: -1,
Out: ioStreams.Out,
ErrOut: ioStreams.ErrOut,
ChunkSize: cmdutil.DefaultChunkSize,
},
}
o.drainer.OnPodDeletionOrEvictionFinished = o.onPodDeletionOrEvictionFinished

View File

@ -366,9 +366,7 @@ func (p *ExecOptions) Run() error {
var sizeQueue remotecommand.TerminalSizeQueue
if t.Raw {
// this call spawns a goroutine to monitor/update the terminal size
sizeQueue = &terminalSizeQueueAdapter{
delegate: t.MonitorSize(t.GetSize()),
}
sizeQueue = t.MonitorSize(t.GetSize())
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
// true
@ -405,18 +403,3 @@ func (p *ExecOptions) Run() error {
return nil
}
type terminalSizeQueueAdapter struct {
delegate term.TerminalSizeQueue
}
func (a *terminalSizeQueueAdapter) Next() *remotecommand.TerminalSize {
next := a.delegate.Next()
if next == nil {
return nil
}
return &remotecommand.TerminalSize{
Width: next.Width,
Height: next.Height,
}
}

View File

@ -206,7 +206,7 @@ func (flags *ExposeServiceFlags) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&flags.Port, "port", flags.Port, i18n.T("The port that the service should serve on. Copied from the resource being exposed, if unspecified"))
cmd.Flags().StringVar(&flags.Type, "type", flags.Type, i18n.T("Type for this service: ClusterIP, NodePort, LoadBalancer, or ExternalName. Default is 'ClusterIP'."))
cmd.Flags().StringVar(&flags.LoadBalancerIP, "load-balancer-ip", flags.LoadBalancerIP, i18n.T("IP to assign to the LoadBalancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)."))
cmd.Flags().StringVar(&flags.Selector, "selector", flags.Selector, i18n.T("A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the resource being exposed."))
cmd.Flags().StringVar(&flags.Selector, "selector", flags.Selector, i18n.T("A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.)"))
cmd.Flags().StringVarP(&flags.Labels, "labels", "l", flags.Labels, "Labels to apply to the service created by this call.")
cmd.Flags().StringVar(&flags.TargetPort, "target-port", flags.TargetPort, i18n.T("Name or number for the port on the container that the service should direct traffic to. Optional."))
cmd.Flags().StringVar(&flags.ExternalIP, "external-ip", flags.ExternalIP, i18n.T("Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP."))

View File

@ -74,7 +74,7 @@ var (
selectorExample = templates.Examples(`
# Set the labels and selector before creating a deployment/service pair
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
kubectl create deployment my-dep --image=nginx -o yaml --dry-run=client | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
kubectl create deployment my-dep -o yaml --dry-run=client | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
)
// NewSelectorOptions returns an initialized SelectorOptions instance

View File

@ -455,12 +455,6 @@ const (
// Transition to WebSockets.
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
// owner: @thockin
// kep: https://kep.k8s.io/5296
//
// Support KYAML output.
KYAMLOutput FeatureGate = "KUBECTL_KYAML"
)
// IsEnabled returns true iff environment variable is set to true.

View File

@ -36,7 +36,7 @@ import (
)
// TODO(knverey): remove this hardcoding once kubectl being built with module support makes BuildInfo available.
const kustomizeVersion = "v5.7.1"
const kustomizeVersion = "v5.5.0"
// Version is a struct for version information
type Version struct {

View File

@ -174,7 +174,7 @@ func getObjAndCheckCondition(ctx context.Context, info *resource.Info, o *WaitOp
return err
})
if err != nil {
if wait.Interrupted(err) { // nolint:staticcheck // SA1019
if errors.Is(err, wait.ErrWaitTimeout) { // nolint:staticcheck // SA1019
return result, false, errWaitTimeoutWithName
}
return result, false, err

View File

@ -113,7 +113,7 @@ func IsDeleted(ctx context.Context, info *resource.Info, o *WaitOptions) (runtim
return err
})
if err != nil {
if wait.Interrupted(err) { // nolint:staticcheck // SA1019
if errors.Is(err, wait.ErrWaitTimeout) { // nolint:staticcheck // SA1019
return gottenObj, false, errWaitTimeoutWithName
}
return gottenObj, false, err

View File

@ -46,7 +46,7 @@ import (
var (
waitLong = templates.LongDesc(i18n.T(`
Wait for a specific condition on one or many resources.
Experimental: Wait for a specific condition on one or many resources.
The command takes multiple resources and waits until the specified condition
is seen in the Status field of every given resource.

View File

@ -53,6 +53,7 @@ import (
rbacv1 "k8s.io/api/rbac/v1"
schedulingv1 "k8s.io/api/scheduling/v1"
storagev1 "k8s.io/api/storage/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/resource"
@ -230,7 +231,7 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr
{Group: certificatesv1beta1.GroupName, Kind: "CertificateSigningRequest"}: &CertificateSigningRequestDescriber{c},
{Group: storagev1.GroupName, Kind: "StorageClass"}: &StorageClassDescriber{c},
{Group: storagev1.GroupName, Kind: "CSINode"}: &CSINodeDescriber{c},
{Group: storagev1.GroupName, Kind: "VolumeAttributesClass"}: &VolumeAttributesClassDescriber{c},
{Group: storagev1beta1.GroupName, Kind: "VolumeAttributesClass"}: &VolumeAttributesClassDescriber{c},
{Group: policyv1beta1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
{Group: policyv1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
{Group: rbacv1.GroupName, Kind: "Role"}: &RoleDescriber{c},
@ -1864,11 +1865,7 @@ func describeContainerBasicInfo(container corev1.Container, status corev1.Contai
func describeContainerPorts(cPorts []corev1.ContainerPort) string {
ports := make([]string, 0, len(cPorts))
for _, cPort := range cPorts {
portStr := fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol)
if cPort.Name != "" {
portStr = fmt.Sprintf("%s (%s)", portStr, cPort.Name)
}
ports = append(ports, portStr)
ports = append(ports, fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol))
}
return strings.Join(ports, ", ")
}
@ -1876,11 +1873,7 @@ func describeContainerPorts(cPorts []corev1.ContainerPort) string {
func describeContainerHostPorts(cPorts []corev1.ContainerPort) string {
ports := make([]string, 0, len(cPorts))
for _, cPort := range cPorts {
portStr := fmt.Sprintf("%d/%s", cPort.HostPort, cPort.Protocol)
if cPort.Name != "" {
portStr = fmt.Sprintf("%s (%s)", portStr, cPort.Name)
}
ports = append(ports, portStr)
ports = append(ports, fmt.Sprintf("%d/%s", cPort.HostPort, cPort.Protocol))
}
return strings.Join(ports, ", ")
}
@ -3445,15 +3438,61 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
return "", err
}
tokens := []corev1.Secret{}
// missingSecrets is the set of all secrets present in the
// serviceAccount but not present in the set of existing secrets.
missingSecrets := sets.New[string]()
secrets := corev1.SecretList{}
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := d.CoreV1().Secrets(namespace).List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceSecrets.String())
}
secrets.Items = append(secrets.Items, newList.Items...)
return newList, nil
})
// errors are tolerated here in order to describe the serviceAccount with all
// of the secrets that it references, even if those secrets cannot be fetched.
if err == nil {
// existingSecrets is the set of all secrets remaining on a
// service account that are not present in the "tokens" slice.
existingSecrets := sets.New[string]()
for _, s := range secrets.Items {
if s.Type == corev1.SecretTypeServiceAccountToken {
name := s.Annotations[corev1.ServiceAccountNameKey]
uid := s.Annotations[corev1.ServiceAccountUIDKey]
if name == serviceAccount.Name && uid == string(serviceAccount.UID) {
tokens = append(tokens, s)
}
}
existingSecrets.Insert(s.Name)
}
for _, s := range serviceAccount.Secrets {
if !existingSecrets.Has(s.Name) {
missingSecrets.Insert(s.Name)
}
}
for _, s := range serviceAccount.ImagePullSecrets {
if !existingSecrets.Has(s.Name) {
missingSecrets.Insert(s.Name)
}
}
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
}
return describeServiceAccount(serviceAccount, events)
return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
}
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, events *corev1.EventList) (string, error) {
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.Set[string], events *corev1.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
@ -3464,16 +3503,28 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, events *corev
var (
emptyHeader = " "
pullHeader = "Image pull secrets:"
mountHeader = "Mountable secrets: "
tokenHeader = "Tokens: "
pullSecretNames = []string{}
pullSecretNames = []string{}
mountSecretNames = []string{}
tokenSecretNames = []string{}
)
for _, s := range serviceAccount.ImagePullSecrets {
pullSecretNames = append(pullSecretNames, s.Name)
}
for _, s := range serviceAccount.Secrets {
mountSecretNames = append(mountSecretNames, s.Name)
}
for _, s := range tokens {
tokenSecretNames = append(tokenSecretNames, s.Name)
}
types := map[string][]string{
pullHeader: pullSecretNames,
pullHeader: pullSecretNames,
mountHeader: mountSecretNames,
tokenHeader: tokenSecretNames,
}
for _, header := range sets.List(sets.KeySet(types)) {
names := types[header]
@ -3482,7 +3533,11 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, events *corev
} else {
prefix := header
for _, name := range names {
w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
if missingSecrets.Has(name) {
w.Write(LEVEL_0, "%s\t%s (not found)\n", prefix, name)
} else {
w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
}
prefix = emptyHeader
}
}
@ -4761,7 +4816,7 @@ type VolumeAttributesClassDescriber struct {
}
func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
vac, err := d.StorageV1().VolumeAttributesClasses().Get(context.TODO(), name, metav1.GetOptions{})
vac, err := d.StorageV1beta1().VolumeAttributesClasses().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return "", err
}
@ -4774,7 +4829,7 @@ func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, descri
return describeVolumeAttributesClass(vac, events)
}
func describeVolumeAttributesClass(vac *storagev1.VolumeAttributesClass, events *corev1.EventList) (string, error) {
func describeVolumeAttributesClass(vac *storagev1beta1.VolumeAttributesClass, events *corev1.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Name:\t%s\n", vac.Name)

View File

@ -43,6 +43,7 @@ import (
policyv1beta1 "k8s.io/api/policy/v1beta1"
schedulingv1 "k8s.io/api/scheduling/v1"
storagev1 "k8s.io/api/storage/v1"
storagev1beta1 "k8s.io/api/storage/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -1371,74 +1372,6 @@ func TestDescribeResources(t *testing.T) {
}
}
func TestDescribeContainerPorts(t *testing.T) {
testCases := []struct {
name string
ports []corev1.ContainerPort
expectedContainer string
expectedHost string
}{
{
name: "no ports",
ports: []corev1.ContainerPort{},
expectedContainer: "",
expectedHost: "",
},
{
name: "container and host port, with name",
ports: []corev1.ContainerPort{
{Name: "web", ContainerPort: 8080, HostPort: 8080, Protocol: corev1.ProtocolTCP},
},
expectedContainer: "8080/TCP (web)",
expectedHost: "8080/TCP (web)",
},
{
name: "container and host port, no name",
ports: []corev1.ContainerPort{
{ContainerPort: 8080, HostPort: 8080, Protocol: corev1.ProtocolTCP},
},
expectedContainer: "8080/TCP",
expectedHost: "8080/TCP",
},
{
name: "multiple ports with mixed configuration",
ports: []corev1.ContainerPort{
{Name: "controller", ContainerPort: 9093, HostPort: 9093, Protocol: corev1.ProtocolTCP},
{ContainerPort: 9092, Protocol: corev1.ProtocolTCP},
{Name: "interbroker", ContainerPort: 9094, HostPort: 9094, Protocol: corev1.ProtocolTCP},
},
expectedContainer: "9093/TCP (controller), 9092/TCP, 9094/TCP (interbroker)",
expectedHost: "9093/TCP (controller), 0/TCP, 9094/TCP (interbroker)",
},
{
name: "all ports with mixed configuration",
ports: []corev1.ContainerPort{
{Name: "controller", ContainerPort: 9093, HostPort: 9093, Protocol: corev1.ProtocolTCP},
{Name: "client", ContainerPort: 9092, HostPort: 9092, Protocol: corev1.ProtocolTCP},
{Name: "interbroker", ContainerPort: 9094, HostPort: 9094, Protocol: corev1.ProtocolTCP},
},
expectedContainer: "9093/TCP (controller), 9092/TCP (client), 9094/TCP (interbroker)",
expectedHost: "9093/TCP (controller), 9092/TCP (client), 9094/TCP (interbroker)",
},
}
for _, tc := range testCases {
t.Run(tc.name+" - container ports", func(t *testing.T) {
result := describeContainerPorts(tc.ports)
if result != tc.expectedContainer {
t.Errorf("describeContainerPorts: expected %q, got %q", tc.expectedContainer, result)
}
})
t.Run(tc.name+" - host ports", func(t *testing.T) {
result := describeContainerHostPorts(tc.ports)
if result != tc.expectedHost {
t.Errorf("describeContainerHostPorts: expected %q, got %q", tc.expectedHost, result)
}
})
}
}
func TestDescribeContainers(t *testing.T) {
trueVal := true
testCases := []struct {
@ -4031,7 +3964,7 @@ Parameters: param1=value1,param2=value2
Events: <none>
`
f := fake.NewSimpleClientset(&storagev1.VolumeAttributesClass{
f := fake.NewSimpleClientset(&storagev1beta1.VolumeAttributesClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
ResourceVersion: "4",
@ -6238,7 +6171,9 @@ func TestDescribeServiceAccount(t *testing.T) {
Namespace: foo
Labels: <none>
Annotations: <none>
Image pull secrets: test-local-ref
Image pull secrets: test-local-ref (not found)
Mountable secrets: test-objectref (not found)
Tokens: <none>
Events: <none>` + "\n"
if out != expectedOut {
t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)

View File

@ -74,9 +74,6 @@ type Helper struct {
// won't drain otherwise
SkipWaitForDeleteTimeoutSeconds int
// EvictErrorRetryDelay is used to control the retry delay after a pod eviction error
EvictErrorRetryDelay time.Duration
// AdditionalFilters are applied sequentially after base drain filters to
// exclude pods using custom logic. Any filter that returns PodDeleteStatus
// with Delete == false will immediately stop execution of further filters.
@ -281,27 +278,37 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
defer cancel()
for _, pod := range pods {
go func(pod corev1.Pod, returnCh chan error) {
activePod := pod
refreshPod := false
for {
switch d.DryRunStrategy {
case cmdutil.DryRunServer:
//nolint:errcheck
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", activePod.Namespace, activePod.Name)
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", pod.Namespace, pod.Name)
default:
if d.OnPodDeletionOrEvictionStarted != nil {
d.OnPodDeletionOrEvictionStarted(&activePod, true)
d.OnPodDeletionOrEvictionStarted(&pod, true)
}
//nolint:errcheck
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", activePod.Namespace, activePod.Name)
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", pod.Namespace, pod.Name)
}
select {
case <-ctx.Done():
// return here or we'll leak a goroutine.
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", activePod.Name, activePod.Namespace, globalTimeout)
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", pod.Name, pod.Namespace, globalTimeout)
return
default:
}
// Create a temporary pod so we don't mutate the pod in the loop.
activePod := pod
if refreshPod {
freshPod, err := getPodFn(pod.Namespace, pod.Name)
// We ignore errors and let eviction sort it out with
// the original pod.
if err == nil {
activePod = *freshPod
}
refreshPod = false
}
err := d.EvictPod(activePod, evictionGroupVersion)
if err == nil {
break
@ -309,9 +316,8 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
returnCh <- nil
return
} else if apierrors.IsTooManyRequests(err) {
//nolint:errcheck
fmt.Fprintf(d.ErrOut, "error when evicting pods/%q -n %q (will retry after %v): %v\n", activePod.Name, activePod.Namespace, d.EvictErrorRetryDelay, err)
time.Sleep(d.EvictErrorRetryDelay)
fmt.Fprintf(d.ErrOut, "error when evicting pods/%q -n %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
time.Sleep(5 * time.Second)
} else if !activePod.ObjectMeta.DeletionTimestamp.IsZero() && apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
// an eviction request in a deleting namespace will throw a forbidden error,
// if the pod is already marked deleted, we can ignore this error, an eviction
@ -320,19 +326,12 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
} else if apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
// an eviction request in a deleting namespace will throw a forbidden error,
// if the pod is not marked deleted, we retry until it is.
//nolint:errcheck
fmt.Fprintf(d.ErrOut, "error when evicting pod %q from terminating namespace %q (will retry after %v): %v\n", activePod.Name, activePod.Namespace, d.EvictErrorRetryDelay, err)
time.Sleep(d.EvictErrorRetryDelay)
fmt.Fprintf(d.ErrOut, "error when evicting pod %q from terminating namespace %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
time.Sleep(5 * time.Second)
} else {
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: %v", activePod.Name, activePod.Namespace, err)
return
}
freshPod, err := getPodFn(activePod.Namespace, activePod.Name)
// we ignore errors and let eviction sort it out with the original pod.
if err == nil {
activePod = *freshPod
}
}
if d.DryRunStrategy == cmdutil.DryRunServer {
returnCh <- nil
@ -340,7 +339,7 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
}
params := waitForDeleteParams{
ctx: ctx,
pods: []corev1.Pod{activePod},
pods: []corev1.Pod{pod},
interval: 1 * time.Second,
timeout: time.Duration(math.MaxInt64),
usingEviction: true,

View File

@ -529,110 +529,3 @@ func TestFilterPods(t *testing.T) {
})
}
}
func TestEvictDuringNamespaceTerminating(t *testing.T) {
testPodUID := types.UID("test-uid")
testPodName := "test-pod"
testNamespace := "default"
retryDelay := 5 * time.Millisecond
globalTimeout := 2 * retryDelay
tests := []struct {
description string
refresh bool
err error
}{
{
description: "Pod refreshed after NamespaceTerminating error",
refresh: true,
err: nil,
},
{
description: "Pod not refreshed after NamespaceTerminating error",
refresh: false,
err: fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", testPodName, testNamespace, globalTimeout),
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
var retry bool
initialPod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: testPodName,
Namespace: testNamespace,
UID: testPodUID,
},
}
// pod with DeletionTimestamp, indicating deletion in progress
deletedPod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: testPodName,
Namespace: testNamespace,
UID: testPodUID,
DeletionTimestamp: &metav1.Time{Time: time.Now()},
},
}
evictPods := []corev1.Pod{*initialPod}
k := fake.NewClientset(initialPod)
addEvictionSupport(t, k, "v1")
// mock eviction to return NamespaceTerminating error
k.PrependReactor("create", "pods", func(action ktest.Action) (bool, runtime.Object, error) {
if action.GetSubresource() != "eviction" {
return false, nil, nil
}
err := apierrors.NewForbidden(
schema.GroupResource{Resource: "pods"},
testPodName,
errors.New("namespace is terminating"),
)
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{
Type: corev1.NamespaceTerminatingCause,
})
return true, nil, err
})
k.PrependReactor("get", "pods", func(action ktest.Action) (bool, runtime.Object, error) {
if !test.refresh {
// for non-refresh test, always return the initial pod
return true, initialPod, nil
}
if retry {
// second call, pod is deleted
return true, nil, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, testPodName)
}
// first call, pod is being deleted
retry = true
return true, deletedPod, nil
})
h := &Helper{
Client: k,
DisableEviction: false,
Out: os.Stdout,
ErrOut: os.Stderr,
Timeout: globalTimeout,
EvictErrorRetryDelay: retryDelay,
}
err := h.DeleteOrEvictPods(evictPods)
if test.err == nil && err != nil {
t.Errorf("expected no error, got: %v", err)
} else if test.err != nil && (err == nil || err.Error() != test.err.Error()) {
t.Errorf("%s: unexpected eviction; actual %v; expected %v", test.description, err, test.err)
}
})
}
}

View File

@ -25,7 +25,6 @@ import (
"time"
"github.com/spf13/cobra"
"k8s.io/utils/ptr"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
@ -319,18 +318,16 @@ func compGetResourceList(restClientGetter genericclioptions.RESTClientGetter, cm
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
o := apiresources.NewAPIResourceOptions(streams)
o.Complete(restClientGetter, cmd, nil)
// Get the list of resources
o.PrintFlags.OutputFormat = ptr.To("name")
o.Output = "name"
o.Cached = true
o.Verbs = []string{"get"}
// TODO:Should set --request-timeout=5s
if err := o.Complete(restClientGetter, cmd, nil); err != nil {
return []string{}
}
// Ignore errors as the output may still be valid
_ = o.RunAPIResources()
o.RunAPIResources()
// Resources can be a comma-separated list. The last element is then
// the one we should complete. For example if toComplete=="pods,secre"

View File

@ -492,101 +492,6 @@ func TestResourceAndPortCompletionFunc(t *testing.T) {
}
}
func TestResourceTypeAndNameCompletionFuncResourceList(t *testing.T) {
// Set up a fake discovery client with some API resources
dc := cmdtesting.NewFakeCachedDiscoveryClient()
dc.PreferredResources = []*metav1.APIResourceList{
{
GroupVersion: "v1",
APIResources: []metav1.APIResource{
{
Name: "pods",
Namespaced: true,
Kind: "Pod",
Verbs: []string{"get", "list"},
},
{
Name: "services",
Namespaced: true,
Kind: "Service",
Verbs: []string{"get", "list"},
},
{
Name: "secrets",
Namespaced: true,
Kind: "Secret",
Verbs: []string{"get", "list"},
},
},
},
{
GroupVersion: "apps/v1",
APIResources: []metav1.APIResource{
{
Name: "deployments",
Namespaced: true,
Kind: "Deployment",
Verbs: []string{"get", "list"},
},
},
},
}
testCases := []struct {
name string
args []string
toComplete string
expectedComps []string
expectedDirective cobra.ShellCompDirective
}{
{
name: "complete resources starting with 's'",
args: []string{},
toComplete: "s",
expectedComps: []string{"secrets", "services"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
name: "complete resources starting with 'p'",
args: []string{},
toComplete: "p",
expectedComps: []string{"pods"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
name: "complete resources starting with 'd'",
args: []string{},
toComplete: "d",
expectedComps: []string{"deployments.apps"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
name: "complete all resources with empty string",
args: []string{},
toComplete: "",
expectedComps: []string{"deployments.apps", "pods", "secrets", "services"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
name: "no matches",
args: []string{},
toComplete: "xyz",
expectedComps: []string{},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tf, cmd := prepareCompletionTest()
tf.WithDiscoveryClient(dc)
compFunc := ResourceTypeAndNameCompletionFunc(tf)
comps, directive := compFunc(cmd, tc.args, tc.toComplete)
checkCompletion(t, comps, tc.expectedComps, directive, tc.expectedDirective)
})
}
}
func setMockFactory(config api.Config) {
clientConfig := clientcmd.NewDefaultClientConfig(config, nil)
testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)

View File

@ -21,28 +21,12 @@ import (
"github.com/moby/term"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/remotecommand"
)
// TerminalSize represents the width and height of a terminal.
// It is the same as staging/src/k8s.io/client-go/tools/remotecommand.TerminalSize.
// Copied to decouple the packages. Terminal-related package should not depend on API client and vice versa.
type TerminalSize struct {
Width uint16
Height uint16
}
// TerminalSizeQueue is capable of returning terminal resize events as they occur.
// It is the same as staging/src/k8s.io/client-go/tools/remotecommand.TerminalSizeQueue.
// Copied to decouple the packages. Terminal-related package should not depend on API client and vice versa.
type TerminalSizeQueue interface {
// Next returns the new terminal size after the terminal has been resized. It returns nil when
// monitoring has been stopped.
Next() *TerminalSize
}
// GetSize returns the current size of the user's terminal. If it isn't a terminal,
// nil is returned.
func (t TTY) GetSize() *TerminalSize {
func (t TTY) GetSize() *remotecommand.TerminalSize {
outFd, isTerminal := term.GetFdInfo(t.Out)
if !isTerminal {
return nil
@ -51,19 +35,19 @@ func (t TTY) GetSize() *TerminalSize {
}
// GetSize returns the current size of the terminal associated with fd.
func GetSize(fd uintptr) *TerminalSize {
func GetSize(fd uintptr) *remotecommand.TerminalSize {
winsize, err := term.GetWinsize(fd)
if err != nil {
runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
return nil
}
return &TerminalSize{Width: winsize.Width, Height: winsize.Height}
return &remotecommand.TerminalSize{Width: winsize.Width, Height: winsize.Height}
}
// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
// initialSizes, or nil if there's no TTY present.
func (t *TTY) MonitorSize(initialSizes ...*TerminalSize) TerminalSizeQueue {
func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecommand.TerminalSizeQueue {
outFd, isTerminal := term.GetFdInfo(t.Out)
if !isTerminal {
return nil
@ -73,7 +57,7 @@ func (t *TTY) MonitorSize(initialSizes ...*TerminalSize) TerminalSizeQueue {
t: *t,
// make it buffered so we can send the initial terminal sizes without blocking, prior to starting
// the streaming below
resizeChan: make(chan TerminalSize, len(initialSizes)),
resizeChan: make(chan remotecommand.TerminalSize, len(initialSizes)),
stopResizing: make(chan struct{}),
}
@ -86,16 +70,16 @@ func (t *TTY) MonitorSize(initialSizes ...*TerminalSize) TerminalSizeQueue {
type sizeQueue struct {
t TTY
// resizeChan receives a Size each time the user's terminal is resized.
resizeChan chan TerminalSize
resizeChan chan remotecommand.TerminalSize
stopResizing chan struct{}
}
// make sure sizeQueue implements the TerminalSizeQueue interface
var _ TerminalSizeQueue = &sizeQueue{}
// make sure sizeQueue implements the resize.TerminalSizeQueue interface
var _ remotecommand.TerminalSizeQueue = &sizeQueue{}
// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
// new event, it sends the current terminal size to resizeChan.
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*TerminalSize) {
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.TerminalSize) {
// send the initial sizes
for i := range initialSizes {
if initialSizes[i] != nil {
@ -103,7 +87,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*TerminalSize) {
}
}
resizeEvents := make(chan TerminalSize, 1)
resizeEvents := make(chan remotecommand.TerminalSize, 1)
monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
@ -134,7 +118,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*TerminalSize) {
// Next returns the new terminal size after the terminal has been resized. It returns nil when
// monitoring has been stopped.
func (s *sizeQueue) Next() *TerminalSize {
func (s *sizeQueue) Next() *remotecommand.TerminalSize {
size, ok := <-s.resizeChan
if !ok {
return nil

View File

@ -25,12 +25,13 @@ import (
"golang.org/x/sys/unix"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/remotecommand"
)
// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the
// terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send
// it to the resizeEvents channel. The goroutine stops when the stop channel is closed.
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
go func() {
defer runtime.HandleCrash()

View File

@ -20,12 +20,13 @@ import (
"time"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/remotecommand"
)
// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send
// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel
// is closed.
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
go func() {
defer runtime.HandleCrash()

View File

@ -23,6 +23,8 @@ import (
wordwrap "github.com/mitchellh/go-wordwrap"
"github.com/moby/term"
"k8s.io/client-go/tools/remotecommand"
)
type wordWrapWriter struct {
@ -68,7 +70,7 @@ func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
}
}
func getTerminalLimitWidth(terminalSize *TerminalSize) uint {
func getTerminalLimitWidth(terminalSize *remotecommand.TerminalSize) uint {
var limit uint
switch {
case terminalSize.Width >= 120:

View File

@ -17,7 +17,7 @@ spec:
spec:
containers:
- name: master
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
image: docker.io/library/redis:5.0.5-alpine
resources:
requests:
cpu: 100m

View File

@ -17,7 +17,7 @@ spec:
spec:
containers:
- name: master
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
image: docker.io/library/redis:5.0.5-alpine
resources:
requests:
cpu: 100m

View File

@ -17,7 +17,7 @@ spec:
spec:
containers:
- name: slave
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
image: docker.io/library/redis:5.0.5-alpine
# We are only implementing the dns option of:
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]

View File

@ -24,7 +24,7 @@ spec:
spec:
containers:
- name: slave
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
image: docker.io/library/redis:5.0.5-alpine
# We are only implementing the dns option of:
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]