Compare commits
49 Commits
v0.34.0-be
...
master
Author | SHA1 | Date |
---|---|---|
|
2a6e583a42 | |
|
9411a867c0 | |
|
aa186336f3 | |
|
4dec168032 | |
|
48317edcc3 | |
|
9f67d52cb6 | |
|
33f48403eb | |
|
ceae52f082 | |
|
74f9adbfa6 | |
|
a628f84f45 | |
|
e98b7e433d | |
|
cce124a8d8 | |
|
9e0a26615e | |
|
4ce6135c24 | |
|
22d999d2fc | |
|
0889899212 | |
|
f4a1e23b35 | |
|
3c19b8deb3 | |
|
cbe60c952e | |
|
12dc58ebf7 | |
|
7b4e1d1699 | |
|
4d28f5cea7 | |
|
4b52eef334 | |
|
3962f4011e | |
|
af99d1e41c | |
|
4f56b5ec5b | |
|
ce4d90902a | |
|
21b32eea57 | |
|
403b4a41e8 | |
|
9fb3eee5e8 | |
|
8185d35b7a | |
|
80ffc392a2 | |
|
3f6dbadba7 | |
|
55aec96de2 | |
|
8f1bcdea60 | |
|
98b4b33964 | |
|
3cb662b4be | |
|
fb7d414ec2 | |
|
e7f17cb570 | |
|
29d75ee358 | |
|
8067f3a1a6 | |
|
483c28b281 | |
|
02042ef887 | |
|
8ed5bb5f0a | |
|
1b3f4fd0f4 | |
|
9c02ed6a6e | |
|
cf54a4ea54 | |
|
a7bf48f663 | |
|
5c587a03ba |
|
@ -1,3 +1,8 @@
|
||||||
|
> ⚠️ **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
|
||||||
|
|
||||||

|

|
||||||
|
|
46
go.mod
46
go.mod
|
@ -27,24 +27,24 @@ require (
|
||||||
github.com/spf13/pflag v1.0.6
|
github.com/spf13/pflag v1.0.6
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
go.yaml.in/yaml/v2 v2.4.2
|
go.yaml.in/yaml/v2 v2.4.2
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.33.0
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0
|
gopkg.in/evanphx/json-patch.v4 v4.13.0
|
||||||
k8s.io/api v0.0.0-20250715090528-7da28ad7db85
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a
|
||||||
k8s.io/apimachinery v0.0.0-20250715090235-1ebcba2516a6
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4
|
||||||
k8s.io/cli-runtime v0.0.0-20250715094709-a28ba5287690
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e
|
||||||
k8s.io/client-go v0.0.0-20250715090929-f78427e36774
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97
|
||||||
k8s.io/component-base v0.0.0-20250715092145-ef3b5b450d5d
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7
|
||||||
k8s.io/component-helpers v0.0.0-20250715092311-11b5d493a527
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc
|
||||||
k8s.io/klog/v2 v2.130.1
|
k8s.io/klog/v2 v2.130.1
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||||
k8s.io/metrics v0.0.0-20250715094530-e16345c4c004
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
|
||||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0
|
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1
|
||||||
sigs.k8s.io/kustomize/kyaml v0.19.0
|
sigs.k8s.io/kustomize/kyaml v0.20.1
|
||||||
sigs.k8s.io/randfill v1.0.0
|
sigs.k8s.io/randfill v1.0.0
|
||||||
sigs.k8s.io/structured-merge-diff/v6 v6.2.0
|
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
|
||||||
sigs.k8s.io/yaml v1.5.0
|
sigs.k8s.io/yaml v1.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -52,16 +52,15 @@ require (
|
||||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.2 // indirect
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/google/btree v1.1.3 // 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-20241029153458-d1b30febd7db // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||||
|
@ -77,20 +76,19 @@ require (
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // 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/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xlab/treeprint v1.2.0 // indirect
|
github.com/xlab/treeprint v1.2.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.40.0 // indirect
|
||||||
golang.org/x/oauth2 v0.27.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.12.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/term v0.30.0 // indirect
|
golang.org/x/term v0.32.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/time v0.9.0 // indirect
|
golang.org/x/time v0.9.0 // indirect
|
||||||
golang.org/x/tools v0.26.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
sigs.k8s.io/kustomize/api v0.19.0 // indirect
|
sigs.k8s.io/kustomize/api v0.20.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
92
go.sum
92
go.sum
|
@ -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/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 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
github.com/fxamacker/cbor/v2 v2.9.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 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/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.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 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
@ -50,8 +50,6 @@ 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/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 h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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=
|
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||||
|
@ -107,8 +105,6 @@ 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/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 h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
|
@ -155,27 +151,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-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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
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-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-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.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.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-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-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-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.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
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/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -188,49 +184,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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
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.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/api v0.0.0-20250715090528-7da28ad7db85 h1:QfiiAaLAQKj+yvMS1ySLpj3UWzxJixBEaWvKNtzwnQM=
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a h1:qS+abmAu2zbGFkbN1vA7LKS07jsXBN1BvTFXFvaGOLI=
|
||||||
k8s.io/api v0.0.0-20250715090528-7da28ad7db85/go.mod h1:MCwhlGL+lP5brf6CuU20fWxaLh/8tUSlu4VM1cOD8Lo=
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a/go.mod h1:/IpJMZ4ur2JBuX+kkBc115bnq09sFfUnbuFNrdEe5yc=
|
||||||
k8s.io/apimachinery v0.0.0-20250715090235-1ebcba2516a6 h1:lH8NMkqxmCWN1CvOiqbfphUcl+EAk95z+3Le9bfEbJ4=
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4 h1:ObQoOWhkcPbMnU7PIHT2pkO2wK66CcBn6vD+77CidHM=
|
||||||
k8s.io/apimachinery v0.0.0-20250715090235-1ebcba2516a6/go.mod h1:TP8uyOuDEOnzGpLOdffo8hPnIjVDljZCxCM/fruV+5M=
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4/go.mod h1:fDax9lidUgmNSmBlzUrSISURQmHpeyamBbKX9jGbJ3g=
|
||||||
k8s.io/cli-runtime v0.0.0-20250715094709-a28ba5287690 h1:1YwSuyjcnhvaAaes2zGdbilWjLeHBxZkJ1hWKz0/IXc=
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e h1:dovWIA2PM0UrJrZdUBV8uy5pExliSBXSFeL0bI6IX6E=
|
||||||
k8s.io/cli-runtime v0.0.0-20250715094709-a28ba5287690/go.mod h1:WykG0uYhov9/a+lenZsbNR4Z5AvFgUzk4bRkfB6SVdY=
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e/go.mod h1:/yp9r2rD6AV7MYM/gmb55/6LttRuURzjhgqbfiFQ0Rg=
|
||||||
k8s.io/client-go v0.0.0-20250715090929-f78427e36774 h1:OJXhumReMNIzlpFEEQvl89u+u7KmQ6fa4I3TpZQYjIg=
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97 h1:6ks7Y8CNm05xZ6eyE0db5IDP54PIyRM3aZhpflG55hI=
|
||||||
k8s.io/client-go v0.0.0-20250715090929-f78427e36774/go.mod h1:y02d1W5RQ3IDA7qs1unUQEkERwkgLrd7fuDANdUN31E=
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97/go.mod h1:xTpANYjBhGsmpO7Gdw8kMt3yQfciVwyRhbqcq77qwyI=
|
||||||
k8s.io/component-base v0.0.0-20250715092145-ef3b5b450d5d h1:dwVoeQ3VARtU56EIoD/h9N5P8HertH7rBD99fESoxN4=
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7 h1:JQ/dO+EIXNQ+y3Vlez6PC6r7T+0JvNQWrBx6CD3jKls=
|
||||||
k8s.io/component-base v0.0.0-20250715092145-ef3b5b450d5d/go.mod h1:Cx9XNntOC5C9BXP8nozdA2se8LRKYnh2rlhglgoCxp8=
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7/go.mod h1:ZZMk2BFRSF/kI9Y5qKmvTk4SJM654XsQ5eJ9cP7mrhw=
|
||||||
k8s.io/component-helpers v0.0.0-20250715092311-11b5d493a527 h1:KUTvlyyLHBRb9BLmlvu2EEsDUM7FATEpZx370bUZ9a8=
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc h1:hEo6RVciD0e0QvtMBgwG9a7fFWb/vkx0Jvw/iQ5i+lQ=
|
||||||
k8s.io/component-helpers v0.0.0-20250715092311-11b5d493a527/go.mod h1:u8G9DUkNxykMmVeOEc6Pf1Tagyjc/nKm0QXG9VQYKY8=
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc/go.mod h1:TmnJ20kJrkgbEqHgHoUxoTksgIUJNCWtv/QM+yBqCF0=
|
||||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
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 h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||||
k8s.io/metrics v0.0.0-20250715094530-e16345c4c004 h1:JMUcdZhgvBQOMwlDWU+DYuxu/msdf2vlP19crwbaF18=
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd h1:GNGeQ8o/xw8TwhavcXmWrGKHd0ez5TRx8qRqyncbFH4=
|
||||||
k8s.io/metrics v0.0.0-20250715094530-e16345c4c004/go.mod h1:oDIgK3jknb/hium+pU4Dca0PB6o53GwvdUA576x1xb8=
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd/go.mod h1:OYYW2zJf2TAQHmxQwQgSsP/i40dCZig4RQkw3o3vFPE=
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
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 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||||
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
|
sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
|
||||||
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
|
sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=
|
||||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0 h1:MWtRRDWCwQEeW2rnJTqJMuV6Agy56P53SkbVoJpN7wA=
|
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 h1:sYJsarwy/SDJfjjLMUqwFDGPwzUtMOQ1i1Ed49+XSbw=
|
||||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0/go.mod h1:XuuZiQF7WdcvZzEYyNww9A0p3LazCKeJmCjeycN8e1I=
|
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
|
sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
|
sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=
|
||||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
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/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||||
sigs.k8s.io/structured-merge-diff/v6 v6.2.0 h1:msyqjP8Nyd5sF3QSmJouFSzcBIdwq4ct8d1/7VSBHIQ=
|
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||||
sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
|
|
|
@ -23,10 +23,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||||
|
@ -61,12 +62,10 @@ var (
|
||||||
// APIResourceOptions is the start of the data required to perform the operation.
|
// 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()
|
// As new fields are added, add them here instead of referencing the cmd.Flags()
|
||||||
type APIResourceOptions struct {
|
type APIResourceOptions struct {
|
||||||
Output string
|
|
||||||
SortBy string
|
SortBy string
|
||||||
APIGroup string
|
APIGroup string
|
||||||
Namespaced bool
|
Namespaced bool
|
||||||
Verbs []string
|
Verbs []string
|
||||||
NoHeaders bool
|
|
||||||
Cached bool
|
Cached bool
|
||||||
Categories []string
|
Categories []string
|
||||||
|
|
||||||
|
@ -76,13 +75,8 @@ type APIResourceOptions struct {
|
||||||
discoveryClient discovery.CachedDiscoveryInterface
|
discoveryClient discovery.CachedDiscoveryInterface
|
||||||
|
|
||||||
genericiooptions.IOStreams
|
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
|
// NewAPIResourceOptions creates the options for APIResource
|
||||||
|
@ -90,6 +84,7 @@ func NewAPIResourceOptions(ioStreams genericiooptions.IOStreams) *APIResourceOpt
|
||||||
return &APIResourceOptions{
|
return &APIResourceOptions{
|
||||||
IOStreams: ioStreams,
|
IOStreams: ioStreams,
|
||||||
Namespaced: true,
|
Namespaced: true,
|
||||||
|
PrintFlags: NewPrintFlags(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,8 +104,7 @@ func NewCmdAPIResources(restClientGetter genericclioptions.RESTClientGetter, ioS
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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).")
|
o.PrintFlags.AddFlags(cmd)
|
||||||
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().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.")
|
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", o.Namespaced, "If false, non-namespaced resources will be returned, otherwise returning namespaced resources by default.")
|
||||||
|
@ -123,10 +117,6 @@ func NewCmdAPIResources(restClientGetter genericclioptions.RESTClientGetter, ioS
|
||||||
|
|
||||||
// Validate checks to the APIResourceOptions to see if there is sufficient information run the command
|
// Validate checks to the APIResourceOptions to see if there is sufficient information run the command
|
||||||
func (o *APIResourceOptions) Validate() error {
|
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")
|
supportedSortTypes := sets.New[string]("", "name", "kind")
|
||||||
if len(o.SortBy) > 0 {
|
if len(o.SortBy) > 0 {
|
||||||
if !supportedSortTypes.Has(o.SortBy) {
|
if !supportedSortTypes.Has(o.SortBy) {
|
||||||
|
@ -151,6 +141,28 @@ func (o *APIResourceOptions) Complete(restClientGetter genericclioptions.RESTCli
|
||||||
o.groupChanged = cmd.Flags().Changed("api-group")
|
o.groupChanged = cmd.Flags().Changed("api-group")
|
||||||
o.nsChanged = cmd.Flags().Changed("namespaced")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +182,7 @@ func (o *APIResourceOptions) RunAPIResources() error {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resources := []groupResource{}
|
var allResources []*metav1.APIResourceList
|
||||||
|
|
||||||
for _, list := range lists {
|
for _, list := range lists {
|
||||||
if len(list.APIResources) == 0 {
|
if len(list.APIResources) == 0 {
|
||||||
|
@ -180,6 +192,14 @@ func (o *APIResourceOptions) RunAPIResources() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
apiList := &metav1.APIResourceList{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "APIResourceList",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
GroupVersion: gv.String(),
|
||||||
|
}
|
||||||
|
var apiResources []metav1.APIResource
|
||||||
for _, resource := range list.APIResources {
|
for _, resource := range list.APIResources {
|
||||||
if len(resource.Verbs) == 0 {
|
if len(resource.Verbs) == 0 {
|
||||||
continue
|
continue
|
||||||
|
@ -200,58 +220,32 @@ func (o *APIResourceOptions) RunAPIResources() error {
|
||||||
if len(o.Categories) > 0 && !sets.New[string](resource.Categories...).HasAll(o.Categories...) {
|
if len(o.Categories) > 0 && !sets.New[string](resource.Categories...).HasAll(o.Categories...) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resources = append(resources, groupResource{
|
// set these because we display a concatenation of these two values under APIVERSION column of human-readable output
|
||||||
APIGroup: gv.Group,
|
resource.Group = gv.Group
|
||||||
APIGroupVersion: gv.String(),
|
resource.Version = gv.Version
|
||||||
APIResource: resource,
|
apiResources = append(apiResources, resource)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
apiList.APIResources = apiResources
|
||||||
|
allResources = append(allResources, apiList)
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.NoHeaders == false && o.Output != "name" {
|
flatList := &metav1.APIResourceList{
|
||||||
if err = printContextHeaders(w, o.Output); err != nil {
|
TypeMeta: metav1.TypeMeta{
|
||||||
return err
|
APIVersion: allResources[0].APIVersion,
|
||||||
|
Kind: allResources[0].Kind,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
for _, resource := range allResources {
|
||||||
|
flatList.APIResources = append(flatList.APIResources, resource.APIResources...)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Stable(sortableResource{resources, o.SortBy})
|
sort.Stable(sortableResource{flatList.APIResources, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) > 0 {
|
err = o.PrintObj(flatList, w)
|
||||||
return errors.NewAggregate(errs)
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
return nil
|
return utilerrors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printContextHeaders(out io.Writer, output string) error {
|
func printContextHeaders(out io.Writer, output string) error {
|
||||||
|
@ -264,7 +258,7 @@ func printContextHeaders(out io.Writer, output string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type sortableResource struct {
|
type sortableResource struct {
|
||||||
resources []groupResource
|
resources []metav1.APIResource
|
||||||
sortBy string
|
sortBy string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +271,7 @@ func (s sortableResource) Less(i, j int) bool {
|
||||||
if ret > 0 {
|
if ret > 0 {
|
||||||
return false
|
return false
|
||||||
} else if ret == 0 {
|
} else if ret == 0 {
|
||||||
return strings.Compare(s.resources[i].APIResource.Name, s.resources[j].APIResource.Name) < 0
|
return strings.Compare(s.resources[i].Name, s.resources[j].Name) < 0
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -285,9 +279,9 @@ func (s sortableResource) Less(i, j int) bool {
|
||||||
func (s sortableResource) compareValues(i, j int) (string, string) {
|
func (s sortableResource) compareValues(i, j int) (string, string) {
|
||||||
switch s.sortBy {
|
switch s.sortBy {
|
||||||
case "name":
|
case "name":
|
||||||
return s.resources[i].APIResource.Name, s.resources[j].APIResource.Name
|
return s.resources[i].Name, s.resources[j].Name
|
||||||
case "kind":
|
case "kind":
|
||||||
return s.resources[i].APIResource.Kind, s.resources[j].APIResource.Kind
|
return s.resources[i].Kind, s.resources[j].Kind
|
||||||
}
|
}
|
||||||
return s.resources[i].APIGroup, s.resources[j].APIGroup
|
return s.resources[i].Group, s.resources[j].Group
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,17 @@ limitations under the License.
|
||||||
package apiresources
|
package apiresources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPIResourcesComplete(t *testing.T) {
|
func TestAPIResourcesComplete(t *testing.T) {
|
||||||
|
@ -48,6 +52,16 @@ See 'kubectl api-resources -h' for help and examples`
|
||||||
if err.Error() != expectedError {
|
if err.Error() != expectedError {
|
||||||
t.Fatalf("Unexpected error: %v\n expected: %v", err, 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) {
|
func TestAPIResourcesValidate(t *testing.T) {
|
||||||
|
@ -61,13 +75,6 @@ func TestAPIResourcesValidate(t *testing.T) {
|
||||||
optionSetupFn: func(o *APIResourceOptions) {},
|
optionSetupFn: func(o *APIResourceOptions) {},
|
||||||
expectedError: "",
|
expectedError: "",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "invalid output",
|
|
||||||
optionSetupFn: func(o *APIResourceOptions) {
|
|
||||||
o.Output = "foo"
|
|
||||||
},
|
|
||||||
expectedError: "--output foo is not available",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "invalid sort by",
|
name: "invalid sort by",
|
||||||
optionSetupFn: func(o *APIResourceOptions) {
|
optionSetupFn: func(o *APIResourceOptions) {
|
||||||
|
@ -322,3 +329,92 @@ 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
/*
|
||||||
|
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()}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"k8s.io/kubectl/pkg/util/completion"
|
"k8s.io/kubectl/pkg/util/completion"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
|
"k8s.io/kubectl/pkg/util/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -300,7 +301,9 @@ func (o *AttachOptions) Run() error {
|
||||||
sizePlusOne.Height++
|
sizePlusOne.Height++
|
||||||
|
|
||||||
// this call spawns a goroutine to monitor/update the terminal size
|
// this call spawns a goroutine to monitor/update the terminal size
|
||||||
sizeQueue = t.MonitorSize(&sizePlusOne, size)
|
sizeQueue = &terminalSizeQueueAdapter{
|
||||||
|
delegate: t.MonitorSize(&sizePlusOne, size),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.DisableStderr = true
|
o.DisableStderr = true
|
||||||
|
@ -357,3 +360,18 @@ 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)
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
"k8s.io/cli-runtime/pkg/resource"
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
||||||
|
"k8s.io/client-go/util/retry"
|
||||||
"k8s.io/component-helpers/auth/rbac/reconciliation"
|
"k8s.io/component-helpers/auth/rbac/reconciliation"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/scheme"
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
|
@ -197,6 +199,9 @@ func (o *ReconcileOptions) Validate() error {
|
||||||
|
|
||||||
// RunReconcile performs the execution
|
// RunReconcile performs the execution
|
||||||
func (o *ReconcileOptions) RunReconcile() error {
|
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 {
|
return o.Visitor.Visit(func(info *resource.Info, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -213,7 +218,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient,
|
Client: o.RBACClient,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
|
result *reconciliation.ReconcileClusterRoleResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -228,7 +240,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient.ClusterRoles(),
|
Client: o.RBACClient.ClusterRoles(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
|
result *reconciliation.ReconcileClusterRoleResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -244,10 +263,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
NamespaceClient: o.NamespaceClient.Namespaces(),
|
NamespaceClient: o.NamespaceClient.Namespaces(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
if err != nil {
|
result *reconciliation.ReconcileClusterRoleBindingResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
||||||
|
|
||||||
case *rbacv1.ClusterRoleBinding:
|
case *rbacv1.ClusterRoleBinding:
|
||||||
|
@ -259,10 +282,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient.ClusterRoleBindings(),
|
Client: o.RBACClient.ClusterRoleBindings(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
if err != nil {
|
result *reconciliation.ReconcileClusterRoleBindingResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
||||||
|
|
||||||
case *rbacv1beta1.Role,
|
case *rbacv1beta1.Role,
|
||||||
|
|
|
@ -129,7 +129,8 @@ func NewCmdConfigSetCredentials(out io.Writer, configAccess clientcmd.ConfigAcce
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCmdConfigSetAuthInfo returns a Command instance for 'config set-credentials' sub command
|
// 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 {
|
func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||||
return NewCmdConfigSetCredentials(out, configAccess)
|
return NewCmdConfigSetCredentials(out, configAccess)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *c
|
||||||
cmdutil.AddValidateFlags(cmd)
|
cmdutil.AddValidateFlags(cmd)
|
||||||
cmdutil.AddDryRunFlag(cmd)
|
cmdutil.AddDryRunFlag(cmd)
|
||||||
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
|
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")
|
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package drain
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ func NewDrainCmdOptions(f cmdutil.Factory, ioStreams genericiooptions.IOStreams)
|
||||||
IOStreams: ioStreams,
|
IOStreams: ioStreams,
|
||||||
drainer: &drain.Helper{
|
drainer: &drain.Helper{
|
||||||
GracePeriodSeconds: -1,
|
GracePeriodSeconds: -1,
|
||||||
|
EvictErrorRetryDelay: 5 * time.Second,
|
||||||
Out: ioStreams.Out,
|
Out: ioStreams.Out,
|
||||||
ErrOut: ioStreams.ErrOut,
|
ErrOut: ioStreams.ErrOut,
|
||||||
ChunkSize: cmdutil.DefaultChunkSize,
|
ChunkSize: cmdutil.DefaultChunkSize,
|
||||||
|
|
|
@ -366,7 +366,9 @@ func (p *ExecOptions) Run() error {
|
||||||
var sizeQueue remotecommand.TerminalSizeQueue
|
var sizeQueue remotecommand.TerminalSizeQueue
|
||||||
if t.Raw {
|
if t.Raw {
|
||||||
// this call spawns a goroutine to monitor/update the terminal size
|
// this call spawns a goroutine to monitor/update the terminal size
|
||||||
sizeQueue = t.MonitorSize(t.GetSize())
|
sizeQueue = &terminalSizeQueueAdapter{
|
||||||
|
delegate: t.MonitorSize(t.GetSize()),
|
||||||
|
}
|
||||||
|
|
||||||
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
|
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
|
||||||
// true
|
// true
|
||||||
|
@ -403,3 +405,18 @@ func (p *ExecOptions) Run() error {
|
||||||
|
|
||||||
return nil
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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.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.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.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 replication controller or replica set.)"))
|
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().StringVarP(&flags.Labels, "labels", "l", flags.Labels, "Labels to apply to the service created by this call.")
|
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.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."))
|
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."))
|
||||||
|
|
|
@ -74,7 +74,7 @@ var (
|
||||||
selectorExample = templates.Examples(`
|
selectorExample = templates.Examples(`
|
||||||
# Set the labels and selector before creating a deployment/service pair
|
# 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 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 -o yaml --dry-run=client | kubectl label --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 -`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSelectorOptions returns an initialized SelectorOptions instance
|
// NewSelectorOptions returns an initialized SelectorOptions instance
|
||||||
|
|
|
@ -455,6 +455,12 @@ const (
|
||||||
// Transition to WebSockets.
|
// Transition to WebSockets.
|
||||||
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
|
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
|
||||||
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_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.
|
// IsEnabled returns true iff environment variable is set to true.
|
||||||
|
|
|
@ -36,7 +36,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(knverey): remove this hardcoding once kubectl being built with module support makes BuildInfo available.
|
// TODO(knverey): remove this hardcoding once kubectl being built with module support makes BuildInfo available.
|
||||||
const kustomizeVersion = "v5.5.0"
|
const kustomizeVersion = "v5.7.1"
|
||||||
|
|
||||||
// Version is a struct for version information
|
// Version is a struct for version information
|
||||||
type Version struct {
|
type Version struct {
|
||||||
|
|
|
@ -174,7 +174,7 @@ func getObjAndCheckCondition(ctx context.Context, info *resource.Info, o *WaitOp
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, wait.ErrWaitTimeout) { // nolint:staticcheck // SA1019
|
if wait.Interrupted(err) { // nolint:staticcheck // SA1019
|
||||||
return result, false, errWaitTimeoutWithName
|
return result, false, errWaitTimeoutWithName
|
||||||
}
|
}
|
||||||
return result, false, err
|
return result, false, err
|
||||||
|
|
|
@ -113,7 +113,7 @@ func IsDeleted(ctx context.Context, info *resource.Info, o *WaitOptions) (runtim
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, wait.ErrWaitTimeout) { // nolint:staticcheck // SA1019
|
if wait.Interrupted(err) { // nolint:staticcheck // SA1019
|
||||||
return gottenObj, false, errWaitTimeoutWithName
|
return gottenObj, false, errWaitTimeoutWithName
|
||||||
}
|
}
|
||||||
return gottenObj, false, err
|
return gottenObj, false, err
|
||||||
|
|
|
@ -46,7 +46,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
waitLong = templates.LongDesc(i18n.T(`
|
waitLong = templates.LongDesc(i18n.T(`
|
||||||
Experimental: Wait for a specific condition on one or many resources.
|
Wait for a specific condition on one or many resources.
|
||||||
|
|
||||||
The command takes multiple resources and waits until the specified condition
|
The command takes multiple resources and waits until the specified condition
|
||||||
is seen in the Status field of every given resource.
|
is seen in the Status field of every given resource.
|
||||||
|
|
|
@ -53,7 +53,6 @@ import (
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
schedulingv1 "k8s.io/api/scheduling/v1"
|
schedulingv1 "k8s.io/api/scheduling/v1"
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
storagev1beta1 "k8s.io/api/storage/v1beta1"
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
@ -231,7 +230,7 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr
|
||||||
{Group: certificatesv1beta1.GroupName, Kind: "CertificateSigningRequest"}: &CertificateSigningRequestDescriber{c},
|
{Group: certificatesv1beta1.GroupName, Kind: "CertificateSigningRequest"}: &CertificateSigningRequestDescriber{c},
|
||||||
{Group: storagev1.GroupName, Kind: "StorageClass"}: &StorageClassDescriber{c},
|
{Group: storagev1.GroupName, Kind: "StorageClass"}: &StorageClassDescriber{c},
|
||||||
{Group: storagev1.GroupName, Kind: "CSINode"}: &CSINodeDescriber{c},
|
{Group: storagev1.GroupName, Kind: "CSINode"}: &CSINodeDescriber{c},
|
||||||
{Group: storagev1beta1.GroupName, Kind: "VolumeAttributesClass"}: &VolumeAttributesClassDescriber{c},
|
{Group: storagev1.GroupName, Kind: "VolumeAttributesClass"}: &VolumeAttributesClassDescriber{c},
|
||||||
{Group: policyv1beta1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
|
{Group: policyv1beta1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
|
||||||
{Group: policyv1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
|
{Group: policyv1.GroupName, Kind: "PodDisruptionBudget"}: &PodDisruptionBudgetDescriber{c},
|
||||||
{Group: rbacv1.GroupName, Kind: "Role"}: &RoleDescriber{c},
|
{Group: rbacv1.GroupName, Kind: "Role"}: &RoleDescriber{c},
|
||||||
|
@ -1865,7 +1864,11 @@ func describeContainerBasicInfo(container corev1.Container, status corev1.Contai
|
||||||
func describeContainerPorts(cPorts []corev1.ContainerPort) string {
|
func describeContainerPorts(cPorts []corev1.ContainerPort) string {
|
||||||
ports := make([]string, 0, len(cPorts))
|
ports := make([]string, 0, len(cPorts))
|
||||||
for _, cPort := range cPorts {
|
for _, cPort := range cPorts {
|
||||||
ports = append(ports, fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol))
|
portStr := fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol)
|
||||||
|
if cPort.Name != "" {
|
||||||
|
portStr = fmt.Sprintf("%s (%s)", portStr, cPort.Name)
|
||||||
|
}
|
||||||
|
ports = append(ports, portStr)
|
||||||
}
|
}
|
||||||
return strings.Join(ports, ", ")
|
return strings.Join(ports, ", ")
|
||||||
}
|
}
|
||||||
|
@ -1873,7 +1876,11 @@ func describeContainerPorts(cPorts []corev1.ContainerPort) string {
|
||||||
func describeContainerHostPorts(cPorts []corev1.ContainerPort) string {
|
func describeContainerHostPorts(cPorts []corev1.ContainerPort) string {
|
||||||
ports := make([]string, 0, len(cPorts))
|
ports := make([]string, 0, len(cPorts))
|
||||||
for _, cPort := range cPorts {
|
for _, cPort := range cPorts {
|
||||||
ports = append(ports, fmt.Sprintf("%d/%s", cPort.HostPort, cPort.Protocol))
|
portStr := fmt.Sprintf("%d/%s", cPort.HostPort, cPort.Protocol)
|
||||||
|
if cPort.Name != "" {
|
||||||
|
portStr = fmt.Sprintf("%s (%s)", portStr, cPort.Name)
|
||||||
|
}
|
||||||
|
ports = append(ports, portStr)
|
||||||
}
|
}
|
||||||
return strings.Join(ports, ", ")
|
return strings.Join(ports, ", ")
|
||||||
}
|
}
|
||||||
|
@ -3438,61 +3445,15 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
|
||||||
return "", err
|
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
|
var events *corev1.EventList
|
||||||
if describerSettings.ShowEvents {
|
if describerSettings.ShowEvents {
|
||||||
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
|
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
|
return describeServiceAccount(serviceAccount, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.Set[string], events *corev1.EventList) (string, error) {
|
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, events *corev1.EventList) (string, error) {
|
||||||
return tabbedString(func(out io.Writer) error {
|
return tabbedString(func(out io.Writer) error {
|
||||||
w := NewPrefixWriter(out)
|
w := NewPrefixWriter(out)
|
||||||
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
|
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
|
||||||
|
@ -3503,28 +3464,16 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []core
|
||||||
var (
|
var (
|
||||||
emptyHeader = " "
|
emptyHeader = " "
|
||||||
pullHeader = "Image pull secrets:"
|
pullHeader = "Image pull secrets:"
|
||||||
mountHeader = "Mountable secrets: "
|
|
||||||
tokenHeader = "Tokens: "
|
|
||||||
|
|
||||||
pullSecretNames = []string{}
|
pullSecretNames = []string{}
|
||||||
mountSecretNames = []string{}
|
|
||||||
tokenSecretNames = []string{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, s := range serviceAccount.ImagePullSecrets {
|
for _, s := range serviceAccount.ImagePullSecrets {
|
||||||
pullSecretNames = append(pullSecretNames, s.Name)
|
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{
|
types := map[string][]string{
|
||||||
pullHeader: pullSecretNames,
|
pullHeader: pullSecretNames,
|
||||||
mountHeader: mountSecretNames,
|
|
||||||
tokenHeader: tokenSecretNames,
|
|
||||||
}
|
}
|
||||||
for _, header := range sets.List(sets.KeySet(types)) {
|
for _, header := range sets.List(sets.KeySet(types)) {
|
||||||
names := types[header]
|
names := types[header]
|
||||||
|
@ -3533,11 +3482,7 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []core
|
||||||
} else {
|
} else {
|
||||||
prefix := header
|
prefix := header
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
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)
|
w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
|
||||||
}
|
|
||||||
prefix = emptyHeader
|
prefix = emptyHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4816,7 +4761,7 @@ type VolumeAttributesClassDescriber struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
|
func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
|
||||||
vac, err := d.StorageV1beta1().VolumeAttributesClasses().Get(context.TODO(), name, metav1.GetOptions{})
|
vac, err := d.StorageV1().VolumeAttributesClasses().Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -4829,7 +4774,7 @@ func (d *VolumeAttributesClassDescriber) Describe(namespace, name string, descri
|
||||||
return describeVolumeAttributesClass(vac, events)
|
return describeVolumeAttributesClass(vac, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func describeVolumeAttributesClass(vac *storagev1beta1.VolumeAttributesClass, events *corev1.EventList) (string, error) {
|
func describeVolumeAttributesClass(vac *storagev1.VolumeAttributesClass, events *corev1.EventList) (string, error) {
|
||||||
return tabbedString(func(out io.Writer) error {
|
return tabbedString(func(out io.Writer) error {
|
||||||
w := NewPrefixWriter(out)
|
w := NewPrefixWriter(out)
|
||||||
w.Write(LEVEL_0, "Name:\t%s\n", vac.Name)
|
w.Write(LEVEL_0, "Name:\t%s\n", vac.Name)
|
||||||
|
|
|
@ -43,7 +43,6 @@ import (
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
schedulingv1 "k8s.io/api/scheduling/v1"
|
schedulingv1 "k8s.io/api/scheduling/v1"
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
storagev1beta1 "k8s.io/api/storage/v1beta1"
|
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -1372,6 +1371,74 @@ 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) {
|
func TestDescribeContainers(t *testing.T) {
|
||||||
trueVal := true
|
trueVal := true
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -3964,7 +4031,7 @@ Parameters: param1=value1,param2=value2
|
||||||
Events: <none>
|
Events: <none>
|
||||||
`
|
`
|
||||||
|
|
||||||
f := fake.NewSimpleClientset(&storagev1beta1.VolumeAttributesClass{
|
f := fake.NewSimpleClientset(&storagev1.VolumeAttributesClass{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
ResourceVersion: "4",
|
ResourceVersion: "4",
|
||||||
|
@ -6171,9 +6238,7 @@ func TestDescribeServiceAccount(t *testing.T) {
|
||||||
Namespace: foo
|
Namespace: foo
|
||||||
Labels: <none>
|
Labels: <none>
|
||||||
Annotations: <none>
|
Annotations: <none>
|
||||||
Image pull secrets: test-local-ref (not found)
|
Image pull secrets: test-local-ref
|
||||||
Mountable secrets: test-objectref (not found)
|
|
||||||
Tokens: <none>
|
|
||||||
Events: <none>` + "\n"
|
Events: <none>` + "\n"
|
||||||
if out != expectedOut {
|
if out != expectedOut {
|
||||||
t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)
|
t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)
|
||||||
|
|
|
@ -74,6 +74,9 @@ type Helper struct {
|
||||||
// won't drain otherwise
|
// won't drain otherwise
|
||||||
SkipWaitForDeleteTimeoutSeconds int
|
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
|
// AdditionalFilters are applied sequentially after base drain filters to
|
||||||
// exclude pods using custom logic. Any filter that returns PodDeleteStatus
|
// exclude pods using custom logic. Any filter that returns PodDeleteStatus
|
||||||
// with Delete == false will immediately stop execution of further filters.
|
// with Delete == false will immediately stop execution of further filters.
|
||||||
|
@ -278,37 +281,27 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
go func(pod corev1.Pod, returnCh chan error) {
|
go func(pod corev1.Pod, returnCh chan error) {
|
||||||
refreshPod := false
|
activePod := pod
|
||||||
for {
|
for {
|
||||||
switch d.DryRunStrategy {
|
switch d.DryRunStrategy {
|
||||||
case cmdutil.DryRunServer:
|
case cmdutil.DryRunServer:
|
||||||
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", pod.Namespace, pod.Name)
|
//nolint:errcheck
|
||||||
|
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", activePod.Namespace, activePod.Name)
|
||||||
default:
|
default:
|
||||||
if d.OnPodDeletionOrEvictionStarted != nil {
|
if d.OnPodDeletionOrEvictionStarted != nil {
|
||||||
d.OnPodDeletionOrEvictionStarted(&pod, true)
|
d.OnPodDeletionOrEvictionStarted(&activePod, true)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", pod.Namespace, pod.Name)
|
//nolint:errcheck
|
||||||
|
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", activePod.Namespace, activePod.Name)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// return here or we'll leak a goroutine.
|
// return here or we'll leak a goroutine.
|
||||||
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", pod.Name, pod.Namespace, globalTimeout)
|
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", activePod.Name, activePod.Namespace, globalTimeout)
|
||||||
return
|
return
|
||||||
default:
|
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)
|
err := d.EvictPod(activePod, evictionGroupVersion)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
|
@ -316,8 +309,9 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
returnCh <- nil
|
returnCh <- nil
|
||||||
return
|
return
|
||||||
} else if apierrors.IsTooManyRequests(err) {
|
} else if apierrors.IsTooManyRequests(err) {
|
||||||
fmt.Fprintf(d.ErrOut, "error when evicting pods/%q -n %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
|
//nolint:errcheck
|
||||||
time.Sleep(5 * time.Second)
|
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)
|
||||||
} else if !activePod.ObjectMeta.DeletionTimestamp.IsZero() && apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
} 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,
|
// 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
|
// if the pod is already marked deleted, we can ignore this error, an eviction
|
||||||
|
@ -326,12 +320,19 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
} else if apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
} else if apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
||||||
// an eviction request in a deleting namespace will throw a forbidden error,
|
// an eviction request in a deleting namespace will throw a forbidden error,
|
||||||
// if the pod is not marked deleted, we retry until it is.
|
// if the pod is not marked deleted, we retry until it is.
|
||||||
fmt.Fprintf(d.ErrOut, "error when evicting pod %q from terminating namespace %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
|
//nolint:errcheck
|
||||||
time.Sleep(5 * time.Second)
|
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)
|
||||||
} else {
|
} else {
|
||||||
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: %v", activePod.Name, activePod.Namespace, err)
|
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: %v", activePod.Name, activePod.Namespace, err)
|
||||||
return
|
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 {
|
if d.DryRunStrategy == cmdutil.DryRunServer {
|
||||||
returnCh <- nil
|
returnCh <- nil
|
||||||
|
@ -339,7 +340,7 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
}
|
}
|
||||||
params := waitForDeleteParams{
|
params := waitForDeleteParams{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
pods: []corev1.Pod{pod},
|
pods: []corev1.Pod{activePod},
|
||||||
interval: 1 * time.Second,
|
interval: 1 * time.Second,
|
||||||
timeout: time.Duration(math.MaxInt64),
|
timeout: time.Duration(math.MaxInt64),
|
||||||
usingEviction: true,
|
usingEviction: true,
|
||||||
|
|
|
@ -529,3 +529,110 @@ 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
@ -318,16 +319,18 @@ func compGetResourceList(restClientGetter genericclioptions.RESTClientGetter, cm
|
||||||
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
|
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
|
||||||
o := apiresources.NewAPIResourceOptions(streams)
|
o := apiresources.NewAPIResourceOptions(streams)
|
||||||
|
|
||||||
o.Complete(restClientGetter, cmd, nil)
|
|
||||||
|
|
||||||
// Get the list of resources
|
// Get the list of resources
|
||||||
o.Output = "name"
|
o.PrintFlags.OutputFormat = ptr.To("name")
|
||||||
o.Cached = true
|
o.Cached = true
|
||||||
o.Verbs = []string{"get"}
|
o.Verbs = []string{"get"}
|
||||||
// TODO:Should set --request-timeout=5s
|
// 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
|
// 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
|
// Resources can be a comma-separated list. The last element is then
|
||||||
// the one we should complete. For example if toComplete=="pods,secre"
|
// the one we should complete. For example if toComplete=="pods,secre"
|
||||||
|
|
|
@ -492,6 +492,101 @@ 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) {
|
func setMockFactory(config api.Config) {
|
||||||
clientConfig := clientcmd.NewDefaultClientConfig(config, nil)
|
clientConfig := clientcmd.NewDefaultClientConfig(config, nil)
|
||||||
testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)
|
testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)
|
||||||
|
|
|
@ -21,12 +21,28 @@ import (
|
||||||
|
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"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,
|
// GetSize returns the current size of the user's terminal. If it isn't a terminal,
|
||||||
// nil is returned.
|
// nil is returned.
|
||||||
func (t TTY) GetSize() *remotecommand.TerminalSize {
|
func (t TTY) GetSize() *TerminalSize {
|
||||||
outFd, isTerminal := term.GetFdInfo(t.Out)
|
outFd, isTerminal := term.GetFdInfo(t.Out)
|
||||||
if !isTerminal {
|
if !isTerminal {
|
||||||
return nil
|
return nil
|
||||||
|
@ -35,19 +51,19 @@ func (t TTY) GetSize() *remotecommand.TerminalSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSize returns the current size of the terminal associated with fd.
|
// GetSize returns the current size of the terminal associated with fd.
|
||||||
func GetSize(fd uintptr) *remotecommand.TerminalSize {
|
func GetSize(fd uintptr) *TerminalSize {
|
||||||
winsize, err := term.GetWinsize(fd)
|
winsize, err := term.GetWinsize(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
|
runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &remotecommand.TerminalSize{Width: winsize.Width, Height: winsize.Height}
|
return &TerminalSize{Width: winsize.Width, Height: winsize.Height}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
|
// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
|
||||||
// initialSizes, or nil if there's no TTY present.
|
// initialSizes, or nil if there's no TTY present.
|
||||||
func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecommand.TerminalSizeQueue {
|
func (t *TTY) MonitorSize(initialSizes ...*TerminalSize) TerminalSizeQueue {
|
||||||
outFd, isTerminal := term.GetFdInfo(t.Out)
|
outFd, isTerminal := term.GetFdInfo(t.Out)
|
||||||
if !isTerminal {
|
if !isTerminal {
|
||||||
return nil
|
return nil
|
||||||
|
@ -57,7 +73,7 @@ func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecom
|
||||||
t: *t,
|
t: *t,
|
||||||
// make it buffered so we can send the initial terminal sizes without blocking, prior to starting
|
// make it buffered so we can send the initial terminal sizes without blocking, prior to starting
|
||||||
// the streaming below
|
// the streaming below
|
||||||
resizeChan: make(chan remotecommand.TerminalSize, len(initialSizes)),
|
resizeChan: make(chan TerminalSize, len(initialSizes)),
|
||||||
stopResizing: make(chan struct{}),
|
stopResizing: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,16 +86,16 @@ func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecom
|
||||||
type sizeQueue struct {
|
type sizeQueue struct {
|
||||||
t TTY
|
t TTY
|
||||||
// resizeChan receives a Size each time the user's terminal is resized.
|
// resizeChan receives a Size each time the user's terminal is resized.
|
||||||
resizeChan chan remotecommand.TerminalSize
|
resizeChan chan TerminalSize
|
||||||
stopResizing chan struct{}
|
stopResizing chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure sizeQueue implements the resize.TerminalSizeQueue interface
|
// make sure sizeQueue implements the TerminalSizeQueue interface
|
||||||
var _ remotecommand.TerminalSizeQueue = &sizeQueue{}
|
var _ TerminalSizeQueue = &sizeQueue{}
|
||||||
|
|
||||||
// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
|
// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
|
||||||
// new event, it sends the current terminal size to resizeChan.
|
// new event, it sends the current terminal size to resizeChan.
|
||||||
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.TerminalSize) {
|
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*TerminalSize) {
|
||||||
// send the initial sizes
|
// send the initial sizes
|
||||||
for i := range initialSizes {
|
for i := range initialSizes {
|
||||||
if initialSizes[i] != nil {
|
if initialSizes[i] != nil {
|
||||||
|
@ -87,7 +103,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.Te
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeEvents := make(chan remotecommand.TerminalSize, 1)
|
resizeEvents := make(chan TerminalSize, 1)
|
||||||
|
|
||||||
monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
|
monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
|
||||||
|
|
||||||
|
@ -118,7 +134,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.Te
|
||||||
|
|
||||||
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
||||||
// monitoring has been stopped.
|
// monitoring has been stopped.
|
||||||
func (s *sizeQueue) Next() *remotecommand.TerminalSize {
|
func (s *sizeQueue) Next() *TerminalSize {
|
||||||
size, ok := <-s.resizeChan
|
size, ok := <-s.resizeChan
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -25,13 +25,12 @@ import (
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the
|
// 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
|
// 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.
|
// it to the resizeEvents channel. The goroutine stops when the stop channel is closed.
|
||||||
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
|
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
|
||||||
go func() {
|
go func() {
|
||||||
defer runtime.HandleCrash()
|
defer runtime.HandleCrash()
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"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
|
// 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
|
// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel
|
||||||
// is closed.
|
// is closed.
|
||||||
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
|
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
|
||||||
go func() {
|
go func() {
|
||||||
defer runtime.HandleCrash()
|
defer runtime.HandleCrash()
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ import (
|
||||||
|
|
||||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type wordWrapWriter struct {
|
type wordWrapWriter struct {
|
||||||
|
@ -70,7 +68,7 @@ func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTerminalLimitWidth(terminalSize *remotecommand.TerminalSize) uint {
|
func getTerminalLimitWidth(terminalSize *TerminalSize) uint {
|
||||||
var limit uint
|
var limit uint
|
||||||
switch {
|
switch {
|
||||||
case terminalSize.Width >= 120:
|
case terminalSize.Width >= 120:
|
||||||
|
|
|
@ -17,7 +17,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: master
|
- name: master
|
||||||
image: docker.io/library/redis:5.0.5-alpine
|
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
|
|
|
@ -17,7 +17,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: master
|
- name: master
|
||||||
image: docker.io/library/redis:5.0.5-alpine
|
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
|
|
|
@ -17,7 +17,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: slave
|
- name: slave
|
||||||
image: docker.io/library/redis:5.0.5-alpine
|
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
|
||||||
# We are only implementing the dns option of:
|
# We are only implementing the dns option of:
|
||||||
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
|
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
|
||||||
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]
|
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]
|
||||||
|
|
|
@ -24,7 +24,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: slave
|
- name: slave
|
||||||
image: docker.io/library/redis:5.0.5-alpine
|
image: registry.k8s.io/e2e-test-images/redis:5.0.5-alpine
|
||||||
# We are only implementing the dns option of:
|
# We are only implementing the dns option of:
|
||||||
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
|
# https://github.com/kubernetes/examples/blob/97c7ed0eb6555a4b667d2877f965d392e00abc45/guestbook/redis-slave/run.sh
|
||||||
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]
|
command: [ "redis-server", "--slaveof", "redis-master", "6379" ]
|
||||||
|
|
Loading…
Reference in New Issue