Compare commits
150 Commits
v0.32.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
2da25b2322 | |
|
110ba5998e | |
|
716737e46a | |
|
8a11631ed9 | |
|
330f86df8e | |
|
55101ca654 | |
|
ab62ac8cf1 | |
|
af5ade99d8 | |
|
4d27286e9c | |
|
9130183f39 | |
|
e808dbbf1f | |
|
579a7a5a35 | |
|
0b4adb247f | |
|
49afb3c466 | |
|
17c0dde6b1 | |
|
bc4c094b08 | |
|
800afb48a7 | |
|
2375a3a9f6 | |
|
18f24e791d | |
|
9c13527bac | |
|
17bb82b84d | |
|
abe43f6e92 | |
|
bb9c5182ea | |
|
4ee16d2b51 | |
|
46d6f63709 | |
|
8500d2979d | |
|
ca5a831a47 | |
|
b011cffff8 | |
|
cb7efba696 | |
|
2be4847754 | |
|
6096dfa3cf | |
|
279ddf3abe | |
|
5ff92a69e3 | |
|
777f5e3cd1 | |
|
4afda566a9 | |
|
c37ca76b9c | |
|
47f13bd18b | |
|
88bb12ba04 | |
|
52ec1da081 | |
|
307936eb9d | |
|
90ee929b88 | |
|
105c831190 | |
|
d35aa2c630 | |
|
f4a8c5b53e | |
|
5bcd2add11 | |
|
bb3c0d9f3a | |
|
3323e167c5 | |
|
3013d81bdf | |
|
38e8d36c38 | |
|
b0f5f0c0aa | |
|
65d852d39c | |
|
7ec7bb7cc1 | |
|
722397942b | |
|
0cdb311ed6 | |
|
4d172bd365 | |
|
285ed6ce48 | |
|
4627533853 | |
|
ae92d5f0bd | |
|
3a0b77ee9b | |
|
9301b2a1f6 | |
|
d40094d4e4 | |
|
46f95a7c68 | |
|
b2ed890887 | |
|
265eadfda5 | |
|
6c0aa1995f | |
|
72b3a7e9b0 | |
|
2e566591a6 | |
|
fec9d5b3d5 | |
|
dc53668bca | |
|
399c585899 | |
|
ece3c8c1d0 | |
|
5b96de1a99 | |
|
5366de04e1 | |
|
7577f36fbc | |
|
f38b1de6c2 | |
|
0d5516dcdc | |
|
5cbdedb625 | |
|
6203603c4a | |
|
f97d1f5267 | |
|
90ba96e4f3 | |
|
ed22800c0c | |
|
920f676703 | |
|
88b66ece35 | |
|
5e1a193aef | |
|
e4ccb76c1a | |
|
277c37ab57 | |
|
2bbe460b18 | |
|
f630fabade | |
|
a108757aa8 | |
|
8c173253d9 | |
|
deef152435 | |
|
cfdaf20447 | |
|
8883001c90 | |
|
24bba46b9d | |
|
cab51f793f | |
|
a9d88dcafc | |
|
561b18e2ed | |
|
594231109f | |
|
1fe7110fa9 | |
|
3a69c59961 | |
|
246e544fc4 | |
|
ef54a58dd7 | |
|
71aa003817 | |
|
4ccb6a52b9 | |
|
480491c908 | |
|
f6d73c948b | |
|
f1e93ac324 | |
|
7f2f474232 | |
|
2e38fc2204 | |
|
2fd8e3617a | |
|
313e838cfa | |
|
79fb20cb8b | |
|
316ed014ab | |
|
d74f0e1af1 | |
|
1ab40ed1c8 | |
|
81562142de | |
|
bcafb59748 | |
|
8af785f4be | |
|
064a840924 | |
|
7346aee2d4 | |
|
d384e79d1a | |
|
7d49790b11 | |
|
7f667b45ca | |
|
cee2a595e4 | |
|
3e68cbc2c4 | |
|
6990be915a | |
|
1613733931 | |
|
be364695f5 | |
|
ddd9a046e7 | |
|
a8a00dbee1 | |
|
120105a0a9 | |
|
088003e485 | |
|
816d854fc8 | |
|
1a5fd321ec | |
|
575c21a9f0 | |
|
94246703f3 | |
|
74629f681a | |
|
86048014d6 | |
|
56c9d286fe | |
|
92bb3cdeda | |
|
beeec3d7a0 | |
|
40fde549ca | |
|
ee2ac712ef | |
|
e26c49a5f9 | |
|
9f75b795dc | |
|
348f12d905 | |
|
61dc2ecb38 | |
|
e2968fb8ad | |
|
608a0553d7 | |
|
169a952417 |
|
@ -10,7 +10,7 @@
|
|||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||
|
||||
ardaguclu
|
||||
eddiezane
|
||||
KnVerey
|
||||
natasha41575
|
||||
mpuckett159
|
||||
soltysh
|
||||
|
|
2
doc.go
2
doc.go
|
@ -14,4 +14,4 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kubectl // import "k8s.io/kubectl"
|
||||
package kubectl
|
||||
|
|
75
go.mod
75
go.mod
|
@ -2,9 +2,9 @@
|
|||
|
||||
module k8s.io/kubectl
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
godebug default=go1.23
|
||||
godebug default=go1.24
|
||||
|
||||
require (
|
||||
github.com/MakeNowJust/heredoc v1.0.0
|
||||
|
@ -13,58 +13,56 @@ require (
|
|||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
github.com/go-openapi/jsonreference v0.20.2
|
||||
github.com/google/gnostic-models v0.6.8
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/google/gnostic-models v0.6.9
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/jonboulle/clockwork v0.5.0
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
|
||||
github.com/lithammer/dedent v1.1.0
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/onsi/ginkgo/v2 v2.21.0
|
||||
github.com/onsi/gomega v1.35.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/sys v0.26.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/sys v0.31.0
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0
|
||||
k8s.io/api v0.0.0-20241108114318-6cc44b8953ae
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba
|
||||
k8s.io/cli-runtime v0.0.0-20241105234034-30b82e78f32f
|
||||
k8s.io/client-go v0.0.0-20241108175443-37045084c2aa
|
||||
k8s.io/component-base v0.0.0-20241108211412-55c45bc78189
|
||||
k8s.io/component-helpers v0.0.0-20241108124227-ea8c930081bd
|
||||
k8s.io/api v0.0.0-20250612195650-7efafe3627c8
|
||||
k8s.io/apimachinery v0.0.0-20250612195403-e0270fe44c97
|
||||
k8s.io/cli-runtime v0.0.0-20250612204029-ea98d716c955
|
||||
k8s.io/client-go v0.0.0-20250612200049-4e82e684120e
|
||||
k8s.io/component-base v0.0.0-20250612201519-d0c00e6471f7
|
||||
k8s.io/component-helpers v0.0.0-20250612201654-2b90e129ba7a
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
|
||||
k8s.io/metrics v0.0.0-20241108213631-40655fb5b014
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff
|
||||
k8s.io/metrics v0.0.0-20250612203843-e8073a5e5ba0
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.5.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0
|
||||
sigs.k8s.io/randfill v1.0.0
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
|
@ -72,24 +70,25 @@ require (
|
|||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.18.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.19.0 // indirect
|
||||
)
|
||||
|
|
142
go.sum
142
go.sum
|
@ -8,14 +8,13 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
|
|||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
||||
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
|
@ -24,8 +23,8 @@ github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2
|
|||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
|
@ -42,32 +41,28 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
|||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
@ -96,8 +91,9 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3
|
|||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
|
@ -114,19 +110,18 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v
|
|||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
@ -138,8 +133,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
|
||||
|
@ -157,29 +152,29 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
|
@ -190,8 +185,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -202,35 +197,38 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.0.0-20241108114318-6cc44b8953ae h1:XX7vEVBchw0xx4YJZ6OyPxOq3e5hX2PTZ5wu8dw0vco=
|
||||
k8s.io/api v0.0.0-20241108114318-6cc44b8953ae/go.mod h1:jw6pQTESH9mdZL2vOK3twojvpPxipl5TpLZpPyl5ZYU=
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba h1:ghB5Iygt6Ge8UyIwW7C1kJx4kP7AUTCL9Qg6GCsUUOY=
|
||||
k8s.io/apimachinery v0.0.0-20241108022104-96b97de8d6ba/go.mod h1:HqhdaJUgQqky29T1V0o2yFkt/pZqLFIDyn9Zi/8rxoY=
|
||||
k8s.io/cli-runtime v0.0.0-20241105234034-30b82e78f32f h1:MV1YnjgZhMzrdCt6HNUhCycAg6uphIgNgbyAyj4DBOI=
|
||||
k8s.io/cli-runtime v0.0.0-20241105234034-30b82e78f32f/go.mod h1:oIRjGaIr68S/l7upHJzyiY4jZEljXlaDA506QlTpDS8=
|
||||
k8s.io/client-go v0.0.0-20241108175443-37045084c2aa h1:iY+s3vejL9yeoJGLOjh314JZtg76ZOjiIETKAnlTUfs=
|
||||
k8s.io/client-go v0.0.0-20241108175443-37045084c2aa/go.mod h1:DojZKPG2ohOKreFao9yo2wFG1IL9OXebT+Q9ytnREY8=
|
||||
k8s.io/component-base v0.0.0-20241108211412-55c45bc78189 h1:RnPIwV9hWuBPXAn69ozUt99OuP/KDuJTIoxYlaJRt4Q=
|
||||
k8s.io/component-base v0.0.0-20241108211412-55c45bc78189/go.mod h1:MPazwHX0pvEjtMIsAy4TYw3vrMHUVe1sy4D1o1Xbcwc=
|
||||
k8s.io/component-helpers v0.0.0-20241108124227-ea8c930081bd h1:gvbf/dWqrHVoCisvo3gEn3TCIgYtsDD5rMzh0fF+GDU=
|
||||
k8s.io/component-helpers v0.0.0-20241108124227-ea8c930081bd/go.mod h1:M/P3FQi94J4zjE2fAWiEIUnCaQNWeM+j9q96LudP3cU=
|
||||
k8s.io/api v0.0.0-20250612195650-7efafe3627c8 h1:K1AnJQBQTKLy2C/up2YSFuuQ+OBucYGcDCBO2cafjlQ=
|
||||
k8s.io/api v0.0.0-20250612195650-7efafe3627c8/go.mod h1:+9QbMyXTXctHAXg3fdhJbuZgyzhYgprCn43M5NqoJzw=
|
||||
k8s.io/apimachinery v0.0.0-20250612195403-e0270fe44c97 h1:h2og30eGCCk1GOEZK6+LNhhlydDcWY3wJaWDIs05xR8=
|
||||
k8s.io/apimachinery v0.0.0-20250612195403-e0270fe44c97/go.mod h1:EZ7eIfFAwky7ktmG4Pu9XWxBxFG++4dxPDOM0GL3abw=
|
||||
k8s.io/cli-runtime v0.0.0-20250612204029-ea98d716c955 h1:/vNS33lahptlYENPecDoe4fD8SOE+aGTxP5wozvK7zw=
|
||||
k8s.io/cli-runtime v0.0.0-20250612204029-ea98d716c955/go.mod h1:Exo7hMRapEHo/QMSGxd1tqaGuZBQpgGZt7Sintr5L/M=
|
||||
k8s.io/client-go v0.0.0-20250612200049-4e82e684120e h1:xoSxEgTvcAD7YG46B6RN1yZx5KhF0YKNe4SSoY+qSQA=
|
||||
k8s.io/client-go v0.0.0-20250612200049-4e82e684120e/go.mod h1:hktzpPyrdfB1WrXOvdnDayNSrngzEwWjiwTGqq6Zjns=
|
||||
k8s.io/component-base v0.0.0-20250612201519-d0c00e6471f7 h1:PmR3IJeL8qbnqdH70lmCLxZjHFr+Cbz5v6VY6ZFlMsI=
|
||||
k8s.io/component-base v0.0.0-20250612201519-d0c00e6471f7/go.mod h1:eMJvxKozNu3AbHhH6mWUJbzNhElacCbAilLeMjxIW5k=
|
||||
k8s.io/component-helpers v0.0.0-20250612201654-2b90e129ba7a h1:AccUaO5Y8Z/rRivu4l0eqZbocGbxHGqiuo19dc0vcPw=
|
||||
k8s.io/component-helpers v0.0.0-20250612201654-2b90e129ba7a/go.mod h1:5Grn35PpLsSHt+WUrQbXI2QN85eBCWnH5nx4fGBbYYU=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/metrics v0.0.0-20241108213631-40655fb5b014 h1:RYq+cPcoidDLJBp8+VXZoDP2wBeDmkW7xgpCSHyAC3c=
|
||||
k8s.io/metrics v0.0.0-20241108213631-40655fb5b014/go.mod h1:WCvk/n97D0QD1cmxQSHVue6BtXf7FlxxtxUbx/MdKUA=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/metrics v0.0.0-20250612203843-e8073a5e5ba0 h1:Eo70T+FEfbC83+3nBRAqcUNQf/YnGlkCYj+c1q+lldY=
|
||||
k8s.io/metrics v0.0.0-20250612203843-e8073a5e5ba0/go.mod h1:wyXjSOSPspEM73pO1WXNVAafLtPfWHMnRPgVrX57niY=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.5.0 h1:o1mtt6vpxsxDYaZKrw3BnEtc+pAjLz7UffnIvHNbvW0=
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.5.0/go.mod h1:AeFCmgCrXzmvjWWaeZCyBp6XzG1Y0w1svYus8GhJEOE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
|
||||
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0 h1:MWtRRDWCwQEeW2rnJTqJMuV6Agy56P53SkbVoJpN7wA=
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0/go.mod h1:XuuZiQF7WdcvZzEYyNww9A0p3LazCKeJmCjeycN8e1I=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
|
|
@ -123,11 +123,11 @@ func NewCmdAPIResources(restClientGetter genericclioptions.RESTClientGetter, ioS
|
|||
|
||||
// Validate checks to the APIResourceOptions to see if there is sufficient information run the command
|
||||
func (o *APIResourceOptions) Validate() error {
|
||||
supportedOutputTypes := sets.NewString("", "wide", "name")
|
||||
supportedOutputTypes := sets.New[string]("", "wide", "name")
|
||||
if !supportedOutputTypes.Has(o.Output) {
|
||||
return fmt.Errorf("--output %v is not available", o.Output)
|
||||
}
|
||||
supportedSortTypes := sets.NewString("", "name", "kind")
|
||||
supportedSortTypes := sets.New[string]("", "name", "kind")
|
||||
if len(o.SortBy) > 0 {
|
||||
if !supportedSortTypes.Has(o.SortBy) {
|
||||
return fmt.Errorf("--sort-by accepts only name or kind")
|
||||
|
@ -193,11 +193,11 @@ func (o *APIResourceOptions) RunAPIResources() error {
|
|||
continue
|
||||
}
|
||||
// filter to resources that support the specified verbs
|
||||
if len(o.Verbs) > 0 && !sets.NewString(resource.Verbs...).HasAll(o.Verbs...) {
|
||||
if len(o.Verbs) > 0 && !sets.New[string](resource.Verbs...).HasAll(o.Verbs...) {
|
||||
continue
|
||||
}
|
||||
// filter to resources that belong to the specified categories
|
||||
if len(o.Categories) > 0 && !sets.NewString(resource.Categories...).HasAll(o.Categories...) {
|
||||
if len(o.Categories) > 0 && !sets.New[string](resource.Categories...).HasAll(o.Categories...) {
|
||||
continue
|
||||
}
|
||||
resources = append(resources, groupResource{
|
||||
|
|
|
@ -2605,7 +2605,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: ""
|
||||
applyset.kubernetes.io/contains-group-kinds: ReplicationController
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1
|
||||
name: my-set
|
||||
|
@ -2639,7 +2638,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: ""
|
||||
applyset.kubernetes.io/contains-group-kinds: ReplicationController,Service
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1
|
||||
name: my-set
|
||||
|
@ -2674,7 +2672,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: ""
|
||||
applyset.kubernetes.io/contains-group-kinds: ReplicationController,Service
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1
|
||||
name: my-set
|
||||
|
@ -2709,7 +2706,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: ""
|
||||
applyset.kubernetes.io/contains-group-kinds: Service
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1
|
||||
name: my-set
|
||||
|
@ -2872,7 +2868,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: test
|
||||
applyset.kubernetes.io/contains-group-kinds: ReplicationController
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-rhp1a-HVAVT_dFgyEygyA1BEB82HPp2o10UiFTpqtAs-v1
|
||||
name: my-set
|
||||
|
@ -3090,7 +3085,6 @@ metadata:
|
|||
applyset.kubernetes.io/additional-namespaces: ""
|
||||
applyset.kubernetes.io/contains-group-resources: replicationcontrollers
|
||||
applyset.kubernetes.io/tooling: kubectl/v0.0.0-master+$Format:%H$
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
applyset.kubernetes.io/id: applyset-0eFHV8ySqp7XoShsGvyWFQD3s96yqwHmzc4e0HR1dsY-v1
|
||||
name: my-set
|
||||
|
|
|
@ -23,8 +23,6 @@ import (
|
|||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
|
@ -61,7 +59,7 @@ const (
|
|||
// patchRetryBackOffPeriod is the period to back off when apply patch results in error.
|
||||
var patchRetryBackOffPeriod = 1 * time.Second
|
||||
|
||||
var createPatchErrFormat = "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
|
||||
var createPatchErrFormat = "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor: %w"
|
||||
|
||||
// Patcher defines options to patch OpenAPI objects.
|
||||
type Patcher struct {
|
||||
|
@ -120,13 +118,13 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, namespace, na
|
|||
// Serialize the current configuration of the object from the server.
|
||||
current, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "serializing current configuration from:\n%v\nfor:", obj)
|
||||
return nil, nil, fmt.Errorf("serializing current configuration from:\n%v\nfor: %w", obj, err)
|
||||
}
|
||||
|
||||
// Retrieve the original configuration of the object from the annotation.
|
||||
original, err := util.GetOriginalConfiguration(obj)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "retrieving original configuration from:\n%v\nfor:", obj)
|
||||
return nil, nil, fmt.Errorf("retrieving original configuration from:\n%v\nfor: %w", obj, err)
|
||||
}
|
||||
|
||||
var patchType types.PatchType
|
||||
|
@ -178,17 +176,17 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, namespace, na
|
|||
patchType = types.StrategicMergePatchType
|
||||
patch, err = p.buildStrategicMergeFromBuiltins(versionedObj, original, modified, current)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, createPatchErrFormat, original, modified, current)
|
||||
return nil, nil, fmt.Errorf(createPatchErrFormat, original, modified, current, err)
|
||||
}
|
||||
} else {
|
||||
if !runtime.IsNotRegisteredError(err) {
|
||||
return nil, nil, errors.Wrapf(err, "getting instance of versioned object for %v:", p.Mapping.GroupVersionKind)
|
||||
return nil, nil, fmt.Errorf("getting instance of versioned object for %v: %w", p.Mapping.GroupVersionKind, err)
|
||||
}
|
||||
|
||||
patchType = types.MergePatchType
|
||||
patch, err = p.buildMergePatch(original, modified, current)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, createPatchErrFormat, original, modified, current)
|
||||
return nil, nil, fmt.Errorf(createPatchErrFormat, original, modified, current, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +198,7 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, namespace, na
|
|||
if p.ResourceVersion != nil {
|
||||
patch, err = addResourceVersion(patch, *p.ResourceVersion)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to insert resourceVersion in patch")
|
||||
return nil, nil, fmt.Errorf("failed to insert resourceVersion in patch: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -307,6 +307,7 @@ func (o *AttachOptions) Run() error {
|
|||
}
|
||||
|
||||
if !o.Quiet {
|
||||
_, _ = fmt.Fprintln(o.ErrOut, "All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.")
|
||||
fmt.Fprintln(o.ErrOut, "If you don't see a command prompt, try pressing enter.")
|
||||
}
|
||||
if err := t.Safe(o.AttachFunc(o, containerToAttach, t.Raw, sizeQueue)); err != nil {
|
||||
|
|
|
@ -242,6 +242,7 @@ func TestAttach(t *testing.T) {
|
|||
pod *corev1.Pod
|
||||
remoteAttachErr bool
|
||||
expectedErr string
|
||||
expectedErrOut []string
|
||||
}{
|
||||
{
|
||||
name: "pod attach",
|
||||
|
@ -251,6 +252,10 @@ func TestAttach(t *testing.T) {
|
|||
attachPath: "/api/" + version + "/namespaces/test/pods/foo/attach",
|
||||
pod: attachPod(),
|
||||
container: "bar",
|
||||
expectedErrOut: []string{
|
||||
"All commands and output from this session will be recorded in container logs, including credentials and sensitive information passed through the command prompt.",
|
||||
"If you don't see a command prompt, try pressing enter.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pod attach error",
|
||||
|
@ -305,10 +310,11 @@ func TestAttach(t *testing.T) {
|
|||
if test.remoteAttachErr {
|
||||
remoteAttach.err = fmt.Errorf("attach error")
|
||||
}
|
||||
streams, _, _, errOut := genericiooptions.NewTestIOStreams()
|
||||
options := &AttachOptions{
|
||||
StreamOptions: exec.StreamOptions{
|
||||
ContainerName: test.container,
|
||||
IOStreams: genericiooptions.NewTestIOStreamsDiscard(),
|
||||
IOStreams: streams,
|
||||
},
|
||||
Attach: remoteAttach,
|
||||
GetPodTimeout: 1000,
|
||||
|
@ -349,6 +355,14 @@ func TestAttach(t *testing.T) {
|
|||
if remoteAttach.url.Query().Get("container") != "bar" {
|
||||
t.Errorf("%s: Did not have query parameters: %s", test.name, remoteAttach.url.Query())
|
||||
}
|
||||
if test.expectedErrOut != nil {
|
||||
for _, expect := range test.expectedErrOut {
|
||||
if !strings.Contains(errOut.String(), expect) {
|
||||
t.Errorf("%s: expected message %s not found, got: %s", test.name, expect, strings.ReplaceAll(errOut.String(), "\n", ""))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,10 +105,10 @@ var (
|
|||
# List all allowed actions in namespace "foo"
|
||||
kubectl auth can-i --list --namespace=foo`)
|
||||
|
||||
resourceVerbs = sets.NewString("get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "use", "bind", "impersonate", "*", "approve", "sign", "escalate", "attest")
|
||||
nonResourceURLVerbs = sets.NewString("get", "put", "post", "head", "options", "delete", "patch", "*")
|
||||
resourceVerbs = sets.New[string]("get", "list", "watch", "create", "update", "patch", "delete", "deletecollection", "use", "bind", "impersonate", "*", "approve", "sign", "escalate", "attest")
|
||||
nonResourceURLVerbs = sets.New[string]("get", "put", "post", "head", "options", "delete", "patch", "*")
|
||||
// holds all the server-supported resources that cannot be discovered by clients. i.e. users and groups for the impersonate verb
|
||||
nonStandardResourceNames = sets.NewString("users", "groups")
|
||||
nonStandardResourceNames = sets.New[string]("users", "groups")
|
||||
)
|
||||
|
||||
// NewCmdCanI returns an initialized Command for 'auth can-i' sub command
|
||||
|
|
|
@ -250,7 +250,7 @@ func printTableSelfSubjectAccessReview(obj runtime.Object, out io.Writer) error
|
|||
}
|
||||
|
||||
if len(ui.Extra) > 0 {
|
||||
for _, k := range sets.StringKeySet(ui.Extra).List() {
|
||||
for _, k := range sets.List(sets.KeySet(ui.Extra)) {
|
||||
v := ui.Extra[k]
|
||||
_, err := fmt.Fprintf(w, "Extra: %s\t%v\n", k, v)
|
||||
if err != nil {
|
||||
|
|
|
@ -75,9 +75,7 @@ func TestWhoAmIRun(t *testing.T) {
|
|||
`{
|
||||
"kind": "SelfSubjectReview",
|
||||
"apiVersion": "authentication.k8s.io/v1",
|
||||
"metadata": {
|
||||
"creationTimestamp": null
|
||||
},
|
||||
"metadata": {},
|
||||
"status": {
|
||||
"userInfo": {
|
||||
"username": "jane.doe",
|
||||
|
@ -131,9 +129,7 @@ func TestWhoAmIRun(t *testing.T) {
|
|||
`{
|
||||
"kind": "SelfSubjectReview",
|
||||
"apiVersion": "authentication.k8s.io/v1beta1",
|
||||
"metadata": {
|
||||
"creationTimestamp": null
|
||||
},
|
||||
"metadata": {},
|
||||
"status": {
|
||||
"userInfo": {
|
||||
"username": "jane.doe",
|
||||
|
@ -186,9 +182,7 @@ func TestWhoAmIRun(t *testing.T) {
|
|||
`{
|
||||
"kind": "SelfSubjectReview",
|
||||
"apiVersion": "authentication.k8s.io/v1",
|
||||
"metadata": {
|
||||
"creationTimestamp": null
|
||||
},
|
||||
"metadata": {},
|
||||
"status": {
|
||||
"userInfo": {
|
||||
"username": "jane.doe",
|
||||
|
|
|
@ -24,13 +24,17 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
|
||||
autoscalingv2client "k8s.io/client-go/kubernetes/typed/autoscaling/v2"
|
||||
"k8s.io/client-go/scale"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
|
@ -43,6 +47,7 @@ import (
|
|||
var (
|
||||
autoscaleLong = templates.LongDesc(i18n.T(`
|
||||
Creates an autoscaler that automatically chooses and sets the number of pods that run in a Kubernetes cluster.
|
||||
The command will attempt to use the autoscaling/v2 API first, in case of an error, it will fall back to autoscaling/v1 API.
|
||||
|
||||
Looks up a deployment, replica set, stateful set, or replication controller by name and creates an autoscaler that uses the given resource as a reference.
|
||||
An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.`))
|
||||
|
@ -78,7 +83,8 @@ type AutoscaleOptions struct {
|
|||
builder *resource.Builder
|
||||
fieldManager string
|
||||
|
||||
HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
|
||||
HPAClientV1 autoscalingv1client.HorizontalPodAutoscalersGetter
|
||||
HPAClientV2 autoscalingv2client.HorizontalPodAutoscalersGetter
|
||||
scaleKindResolver scale.ScaleKindResolver
|
||||
|
||||
genericiooptions.IOStreams
|
||||
|
@ -157,7 +163,8 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.HPAClient = kubeClient.AutoscalingV1()
|
||||
o.HPAClientV2 = kubeClient.AutoscalingV2()
|
||||
o.HPAClientV1 = kubeClient.AutoscalingV1()
|
||||
|
||||
o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
|
@ -186,7 +193,6 @@ func (o *AutoscaleOptions) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Run performs the execution
|
||||
func (o *AutoscaleOptions) Run() error {
|
||||
r := o.builder.
|
||||
Unstructured().
|
||||
|
@ -212,44 +218,19 @@ func (o *AutoscaleOptions) Run() error {
|
|||
return fmt.Errorf("cannot autoscale a %v: %v", mapping.GroupVersionKind.Kind, err)
|
||||
}
|
||||
|
||||
hpa := o.createHorizontalPodAutoscaler(info.Name, mapping)
|
||||
|
||||
if err := o.Recorder.Record(hpa); err != nil {
|
||||
klog.V(4).Infof("error recording current command: %v", err)
|
||||
}
|
||||
|
||||
if o.dryRunStrategy == cmdutil.DryRunClient {
|
||||
count++
|
||||
|
||||
printer, err := o.ToPrinter("created")
|
||||
if err != nil {
|
||||
// handles the creation of HorizontalPodAutoscaler objects for both v2 and v1 APIs.
|
||||
// If v2 API fails, try to create and handle HorizontalPodAutoscaler using v1 API
|
||||
hpaV2 := o.createHorizontalPodAutoscalerV2(info.Name, mapping)
|
||||
if err := o.handleHPA(hpaV2); err != nil {
|
||||
klog.V(1).Infof("Encountered an error with the v2 HorizontalPodAutoscaler: %v. "+
|
||||
"Falling back to try the v1 HorizontalPodAutoscaler", err)
|
||||
hpaV1 := o.createHorizontalPodAutoscalerV1(info.Name, mapping)
|
||||
if err := o.handleHPA(hpaV1); err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(hpa, o.Out)
|
||||
}
|
||||
|
||||
if err := util.CreateOrUpdateAnnotation(o.createAnnotation, hpa, scheme.DefaultJSONEncoder()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createOptions := metav1.CreateOptions{}
|
||||
if o.fieldManager != "" {
|
||||
createOptions.FieldManager = o.fieldManager
|
||||
}
|
||||
if o.dryRunStrategy == cmdutil.DryRunServer {
|
||||
createOptions.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
actualHPA, err := o.HPAClient.HorizontalPodAutoscalers(o.namespace).Create(context.TODO(), hpa, createOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count++
|
||||
printer, err := o.ToPrinter("autoscaled")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(actualHPA, o.Out)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -260,7 +241,96 @@ func (o *AutoscaleOptions) Run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) createHorizontalPodAutoscaler(refName string, mapping *meta.RESTMapping) *autoscalingv1.HorizontalPodAutoscaler {
|
||||
// handleHPA handles the creation and management of a single HPA object.
|
||||
func (o *AutoscaleOptions) handleHPA(hpa runtime.Object) error {
|
||||
if err := o.Recorder.Record(hpa); err != nil {
|
||||
return fmt.Errorf("error recording current command: %w", err)
|
||||
}
|
||||
|
||||
if o.dryRunStrategy == cmdutil.DryRunClient {
|
||||
printer, err := o.ToPrinter("created")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(hpa, o.Out)
|
||||
}
|
||||
|
||||
if err := util.CreateOrUpdateAnnotation(o.createAnnotation, hpa, scheme.DefaultJSONEncoder()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
createOptions := metav1.CreateOptions{}
|
||||
if o.fieldManager != "" {
|
||||
createOptions.FieldManager = o.fieldManager
|
||||
}
|
||||
if o.dryRunStrategy == cmdutil.DryRunServer {
|
||||
createOptions.DryRun = []string{metav1.DryRunAll}
|
||||
}
|
||||
|
||||
var actualHPA runtime.Object
|
||||
var err error
|
||||
switch typedHPA := hpa.(type) {
|
||||
case *autoscalingv2.HorizontalPodAutoscaler:
|
||||
actualHPA, err = o.HPAClientV2.HorizontalPodAutoscalers(o.namespace).Create(context.TODO(), typedHPA, createOptions)
|
||||
case *autoscalingv1.HorizontalPodAutoscaler:
|
||||
actualHPA, err = o.HPAClientV1.HorizontalPodAutoscalers(o.namespace).Create(context.TODO(), typedHPA, createOptions)
|
||||
default:
|
||||
return fmt.Errorf("unsupported HorizontalPodAutoscaler type %T", hpa)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter("autoscaled")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(actualHPA, o.Out)
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) createHorizontalPodAutoscalerV2(refName string, mapping *meta.RESTMapping) *autoscalingv2.HorizontalPodAutoscaler {
|
||||
name := o.Name
|
||||
if len(name) == 0 {
|
||||
name = refName
|
||||
}
|
||||
|
||||
scaler := autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: mapping.GroupVersionKind.GroupVersion().String(),
|
||||
Kind: mapping.GroupVersionKind.Kind,
|
||||
Name: refName,
|
||||
},
|
||||
MaxReplicas: o.Max,
|
||||
},
|
||||
}
|
||||
|
||||
if o.Min > 0 {
|
||||
scaler.Spec.MinReplicas = &o.Min
|
||||
}
|
||||
|
||||
if o.CPUPercent >= 0 {
|
||||
scaler.Spec.Metrics = []autoscalingv2.MetricSpec{
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: &o.CPUPercent,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &scaler
|
||||
}
|
||||
|
||||
func (o *AutoscaleOptions) createHorizontalPodAutoscalerV1(refName string, mapping *meta.RESTMapping) *autoscalingv1.HorizontalPodAutoscaler {
|
||||
name := o.Name
|
||||
if len(name) == 0 {
|
||||
name = refName
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
autoscalingv2 "k8s.io/api/autoscaling/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
@ -87,14 +89,15 @@ func TestAutoscaleValidate(t *testing.T) {
|
|||
}
|
||||
|
||||
type createHorizontalPodAutoscalerTestCase struct {
|
||||
name string
|
||||
options *AutoscaleOptions
|
||||
refName string
|
||||
mapping *meta.RESTMapping
|
||||
expectedHPA *autoscalingv1.HorizontalPodAutoscaler
|
||||
name string
|
||||
options *AutoscaleOptions
|
||||
refName string
|
||||
mapping *meta.RESTMapping
|
||||
expectedHPAV2 *autoscalingv2.HorizontalPodAutoscaler
|
||||
expectedHPAV1 *autoscalingv1.HorizontalPodAutoscaler
|
||||
}
|
||||
|
||||
func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
||||
func TestCreateHorizontalPodAutoscalerV2(t *testing.T) {
|
||||
tests := []createHorizontalPodAutoscalerTestCase{
|
||||
{
|
||||
name: "create with all options",
|
||||
|
@ -112,7 +115,279 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-1",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(10),
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#ResourceMetricSource
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricTarget
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: ptr.To(int32(80)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create without min replicas",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "custom-name-2",
|
||||
Max: 10,
|
||||
Min: -1,
|
||||
CPUPercent: 80,
|
||||
},
|
||||
refName: "deployment-2",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-2",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-2",
|
||||
},
|
||||
MinReplicas: nil,
|
||||
MaxReplicas: int32(10),
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#ResourceMetricSource
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricTarget
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: ptr.To(int32(80)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create without max replicas",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "custom-name-3",
|
||||
Max: -1,
|
||||
Min: 2,
|
||||
CPUPercent: 80,
|
||||
},
|
||||
refName: "deployment-3",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-3",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-3",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(-1),
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#ResourceMetricSource
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricTarget
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: ptr.To(int32(80)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create without cpu utilization",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "custom-name-4",
|
||||
Max: 10,
|
||||
Min: 2,
|
||||
CPUPercent: -1,
|
||||
},
|
||||
refName: "deployment-4",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-4",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-4",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create with replicaset reference",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "replicaset-hpa",
|
||||
Max: 5,
|
||||
Min: 1,
|
||||
CPUPercent: 70,
|
||||
},
|
||||
refName: "frontend",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "ReplicaSet",
|
||||
},
|
||||
},
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicaset-hpa",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "frontend",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(1)),
|
||||
MaxReplicas: int32(5),
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#ResourceMetricSource
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricTarget
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: ptr.To(int32(70)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create with statefulset reference",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "statefulset-hpa",
|
||||
Max: 8,
|
||||
Min: 2,
|
||||
CPUPercent: 60,
|
||||
},
|
||||
refName: "web",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "StatefulSet",
|
||||
},
|
||||
},
|
||||
expectedHPAV2: &autoscalingv2.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "statefulset-hpa",
|
||||
},
|
||||
Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "StatefulSet",
|
||||
Name: "web",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(8),
|
||||
Metrics: []autoscalingv2.MetricSpec{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec
|
||||
{
|
||||
Type: autoscalingv2.ResourceMetricSourceType,
|
||||
Resource: &autoscalingv2.ResourceMetricSource{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#ResourceMetricSource
|
||||
Name: corev1.ResourceCPU,
|
||||
Target: autoscalingv2.MetricTarget{
|
||||
// Reference: https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricTarget
|
||||
Type: autoscalingv2.UtilizationMetricType,
|
||||
AverageUtilization: ptr.To(int32(60)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
hpa := tc.options.createHorizontalPodAutoscalerV2(tc.refName, tc.mapping)
|
||||
assert.Equal(t, tc.expectedHPAV2, hpa)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateHorizontalPodAutoscalerV1(t *testing.T) {
|
||||
tests := []createHorizontalPodAutoscalerTestCase{
|
||||
{
|
||||
name: "create with all options",
|
||||
options: &AutoscaleOptions{
|
||||
Name: "custom-name",
|
||||
Max: 10,
|
||||
Min: 2,
|
||||
CPUPercent: 80,
|
||||
},
|
||||
refName: "deployment-1",
|
||||
mapping: &meta.RESTMapping{
|
||||
GroupVersionKind: schema.GroupVersionKind{
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name",
|
||||
},
|
||||
|
@ -144,7 +419,7 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-2",
|
||||
},
|
||||
|
@ -176,7 +451,7 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-3",
|
||||
},
|
||||
|
@ -208,7 +483,7 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-4",
|
||||
},
|
||||
|
@ -218,9 +493,8 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "Deployment",
|
||||
Name: "deployment-4",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(10),
|
||||
TargetCPUUtilizationPercentage: nil,
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -240,7 +514,7 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "ReplicaSet",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicaset-hpa",
|
||||
},
|
||||
|
@ -272,7 +546,7 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
Kind: "StatefulSet",
|
||||
},
|
||||
},
|
||||
expectedHPA: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "statefulset-hpa",
|
||||
},
|
||||
|
@ -291,8 +565,8 @@ func TestCreateHorizontalPodAutoscaler(t *testing.T) {
|
|||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
hpa := tc.options.createHorizontalPodAutoscaler(tc.refName, tc.mapping)
|
||||
assert.Equal(t, tc.expectedHPA, hpa)
|
||||
hpa := tc.options.createHorizontalPodAutoscalerV1(tc.refName, tc.mapping)
|
||||
assert.Equal(t, tc.expectedHPAV1, hpa)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ import (
|
|||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/cmd/version"
|
||||
"k8s.io/kubectl/pkg/cmd/wait"
|
||||
"k8s.io/kubectl/pkg/kuberc"
|
||||
utilcomp "k8s.io/kubectl/pkg/util/completion"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
|
@ -82,8 +83,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/cmd/kustomize"
|
||||
)
|
||||
|
||||
const kubectlCmdHeaders = "KUBECTL_COMMAND_HEADERS"
|
||||
|
||||
type KubectlOptions struct {
|
||||
PluginHandler PluginHandler
|
||||
Arguments []string
|
||||
|
@ -142,25 +141,23 @@ func NewDefaultKubectlCommandWithArgs(o KubectlOptions) *cobra.Command {
|
|||
}
|
||||
}
|
||||
} else if err == nil {
|
||||
if !cmdutil.CmdPluginAsSubcommand.IsDisabled() {
|
||||
// Command exists(e.g. kubectl create), but it is not certain that
|
||||
// subcommand also exists (e.g. kubectl create networkpolicy)
|
||||
// we also have to eliminate kubectl create -f
|
||||
if IsSubcommandPluginAllowed(foundCmd.Name()) && len(foundArgs) >= 1 && !strings.HasPrefix(foundArgs[0], "-") {
|
||||
subcommand := foundArgs[0]
|
||||
builtinSubcmdExist := false
|
||||
for _, subcmd := range foundCmd.Commands() {
|
||||
if subcmd.Name() == subcommand {
|
||||
builtinSubcmdExist = true
|
||||
break
|
||||
}
|
||||
// Command exists(e.g. kubectl create), but it is not certain that
|
||||
// subcommand also exists (e.g. kubectl create networkpolicy)
|
||||
// we also have to eliminate kubectl create -f
|
||||
if IsSubcommandPluginAllowed(foundCmd.Name()) && len(foundArgs) >= 1 && !strings.HasPrefix(foundArgs[0], "-") {
|
||||
subcommand := foundArgs[0]
|
||||
builtinSubcmdExist := false
|
||||
for _, subcmd := range foundCmd.Commands() {
|
||||
if subcmd.Name() == subcommand {
|
||||
builtinSubcmdExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !builtinSubcmdExist {
|
||||
if err := HandlePluginCommand(o.PluginHandler, cmdPathPieces, len(cmdPathPieces)-len(foundArgs)+1); err != nil {
|
||||
fmt.Fprintf(o.IOStreams.ErrOut, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if !builtinSubcmdExist {
|
||||
if err := HandlePluginCommand(o.PluginHandler, cmdPathPieces, len(cmdPathPieces)-len(foundArgs)+1); err != nil {
|
||||
fmt.Fprintf(o.IOStreams.ErrOut, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -361,6 +358,11 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command {
|
|||
|
||||
flags.BoolVar(&warningsAsErrors, "warnings-as-errors", warningsAsErrors, "Treat warnings received from the server as errors and exit with a non-zero exit code")
|
||||
|
||||
pref := kuberc.NewPreferences()
|
||||
if !cmdutil.KubeRC.IsDisabled() {
|
||||
pref.AddFlags(flags)
|
||||
}
|
||||
|
||||
kubeConfigFlags := o.ConfigFlags
|
||||
if kubeConfigFlags == nil {
|
||||
kubeConfigFlags = defaultConfigFlags().WithWarningPrinter(o.IOStreams)
|
||||
|
@ -383,6 +385,8 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command {
|
|||
// Avoid import cycle by setting ValidArgsFunction here instead of in NewCmdGet()
|
||||
getCmd := get.NewCmdGet("kubectl", f, o.IOStreams)
|
||||
getCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
|
||||
debugCmd := debug.NewCmdDebug(f, o.IOStreams)
|
||||
debugCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
|
||||
|
||||
groups := templates.CommandGroups{
|
||||
{
|
||||
|
@ -434,7 +438,7 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command {
|
|||
proxyCmd,
|
||||
cp.NewCmdCp(f, o.IOStreams),
|
||||
auth.NewCmdAuth(f, o.IOStreams),
|
||||
debug.NewCmdDebug(f, o.IOStreams),
|
||||
debugCmd,
|
||||
events.NewCmdEvents(f, o.IOStreams),
|
||||
},
|
||||
},
|
||||
|
@ -490,6 +494,15 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command {
|
|||
// Stop warning about normalization of flags. That makes it possible to
|
||||
// add the klog flags later.
|
||||
cmds.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc)
|
||||
|
||||
if !cmdutil.KubeRC.IsDisabled() {
|
||||
_, err := pref.Apply(cmds, o.Arguments, o.IOStreams.ErrOut)
|
||||
if err != nil {
|
||||
fmt.Fprintf(o.IOStreams.ErrOut, "error occurred while applying preferences %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return cmds
|
||||
}
|
||||
|
||||
|
@ -507,12 +520,9 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command {
|
|||
//
|
||||
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/859-kubectl-headers
|
||||
func addCmdHeaderHooks(cmds *cobra.Command, kubeConfigFlags *genericclioptions.ConfigFlags) {
|
||||
// If the feature gate env var is set to "false", then do no add kubectl command headers.
|
||||
if value, exists := os.LookupEnv(kubectlCmdHeaders); exists {
|
||||
if value == "false" || value == "0" {
|
||||
klog.V(5).Infoln("kubectl command headers turned off")
|
||||
return
|
||||
}
|
||||
if cmdutil.CmdHeaders.IsDisabled() {
|
||||
klog.V(5).Infoln("kubectl command headers turned off")
|
||||
return
|
||||
}
|
||||
klog.V(5).Infoln("kubectl command headers turned on")
|
||||
crt := &genericclioptions.CommandHeaderRoundTripper{}
|
||||
|
@ -566,3 +576,29 @@ func registerCompletionFuncForGlobalFlags(cmd *cobra.Command, f cmdutil.Factory)
|
|||
return utilcomp.ListUsersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
|
||||
}))
|
||||
}
|
||||
|
||||
// GetLogVerbosity parses the provided command-line arguments to determine
|
||||
// the verbosity level for logging. Returns string representing the verbosity
|
||||
// level, or 0 if no verbosity flag is specified.
|
||||
func GetLogVerbosity(args []string) string {
|
||||
for i, arg := range args {
|
||||
if arg == "--" {
|
||||
// flags after "--" does not represent any flag of
|
||||
// the command. We should short cut the iteration in here.
|
||||
break
|
||||
}
|
||||
|
||||
if arg == "--v" || arg == "-v" {
|
||||
if i+1 < len(args) {
|
||||
return args[i+1]
|
||||
}
|
||||
} else if strings.Contains(arg, "--v=") || strings.Contains(arg, "-v=") {
|
||||
parg := strings.Split(arg, "=")
|
||||
if len(parg) > 1 && parg[1] != "" {
|
||||
return parg[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "0"
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||
"k8s.io/kubectl/pkg/cmd/plugin"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
)
|
||||
|
||||
func TestNormalizationFuncGlobalExistence(t *testing.T) {
|
||||
|
@ -381,10 +382,6 @@ func TestKubectlCommandHeadersHooks(t *testing.T) {
|
|||
envVar: "false",
|
||||
addsHooks: false,
|
||||
},
|
||||
"zero env var value; hooks NOT added": {
|
||||
envVar: "0",
|
||||
addsHooks: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range tests {
|
||||
|
@ -394,7 +391,7 @@ func TestKubectlCommandHeadersHooks(t *testing.T) {
|
|||
if kubeConfigFlags.WrapConfigFn != nil {
|
||||
t.Fatal("expected initial nil WrapConfigFn")
|
||||
}
|
||||
t.Setenv(kubectlCmdHeaders, testCase.envVar)
|
||||
t.Setenv(string(cmdutil.CmdHeaders), testCase.envVar)
|
||||
addCmdHeaderHooks(cmds, kubeConfigFlags)
|
||||
// Valdidate whether the hooks were added.
|
||||
if testCase.addsHooks && kubeConfigFlags.WrapConfigFn == nil {
|
||||
|
|
|
@ -69,7 +69,6 @@ func Example_view() {
|
|||
// name: federal-context
|
||||
// current-context: federal-context
|
||||
// kind: Config
|
||||
// preferences: {}
|
||||
// users:
|
||||
// - name: red-user
|
||||
// user:
|
||||
|
|
|
@ -89,7 +89,7 @@ func NewCmdConfigGetContexts(streams genericiooptions.IOStreams, configAccess cl
|
|||
|
||||
// Complete assigns GetContextsOptions from the args.
|
||||
func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
supportedOutputTypes := sets.NewString("", "name")
|
||||
supportedOutputTypes := sets.New[string]("", "name")
|
||||
if !supportedOutputTypes.Has(o.outputFormat) {
|
||||
return fmt.Errorf("--output %v is not available in kubectl config get-contexts; resetting to default output format", o.outputFormat)
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func newNavigationSteps(path string) (*navigationSteps, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions))
|
||||
nextPart := findNameStep(individualParts[currPartIndex:], sets.KeySet(mapValueOptions))
|
||||
|
||||
steps = append(steps, navigationStep{nextPart, mapValueType})
|
||||
currPartIndex += len(strings.Split(nextPart, "."))
|
||||
|
@ -98,7 +98,7 @@ func (s *navigationSteps) moreStepsRemaining() bool {
|
|||
|
||||
// findNameStep takes the list of parts and a set of valid tags that can be used after the name. It then walks the list of parts
|
||||
// until it find a valid "next" tag or until it reaches the end of the parts and then builds the name back up out of the individual parts
|
||||
func findNameStep(parts []string, typeOptions sets.String) string {
|
||||
func findNameStep(parts []string, typeOptions sets.Set[string]) string {
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, er
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
func findKnownValue(parts []string, valueOptions sets.String) int {
|
||||
func findKnownValue(parts []string, valueOptions sets.Set[string]) int {
|
||||
for i := range parts {
|
||||
if valueOptions.Has(parts[i]) {
|
||||
return i
|
||||
|
|
|
@ -85,7 +85,6 @@ contexts:
|
|||
name: my-cluster
|
||||
current-context: minikube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: minikube
|
||||
user:
|
||||
|
@ -165,7 +164,6 @@ contexts:
|
|||
name: my-cluster
|
||||
current-context: minikube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: minikube
|
||||
user:
|
||||
|
@ -247,7 +245,6 @@ contexts:
|
|||
name: minikube
|
||||
current-context: minikube
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: minikube
|
||||
user:
|
||||
|
@ -272,7 +269,6 @@ contexts:
|
|||
name: my-cluster
|
||||
current-context: my-cluster
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: mu-cluster
|
||||
user:
|
||||
|
|
|
@ -280,7 +280,7 @@ func (o *CopyOptions) checkDestinationIsDir(dest fileSpec) error {
|
|||
Executor: &exec.DefaultRemoteExecutor{},
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
done := make(chan error)
|
||||
|
|
|
@ -116,7 +116,7 @@ var (
|
|||
func AddSpecialVerb(verb string, gr schema.GroupResource) {
|
||||
resources, ok := specialVerbs[verb]
|
||||
if !ok {
|
||||
resources = make([]schema.GroupResource, 1)
|
||||
resources = make([]schema.GroupResource, 0, 1)
|
||||
}
|
||||
resources = append(resources, gr)
|
||||
specialVerbs[verb] = resources
|
||||
|
@ -425,7 +425,7 @@ func generateResourcePolicyRules(mapper meta.RESTMapper, verbs []string, resourc
|
|||
|
||||
// Create separate rule for each of the api group.
|
||||
rules := []rbacv1.PolicyRule{}
|
||||
for _, g := range sets.StringKeySet(groupResourceMapping).List() {
|
||||
for _, g := range sets.List(sets.KeySet(groupResourceMapping)) {
|
||||
rule := rbacv1.PolicyRule{}
|
||||
rule.Verbs = verbs
|
||||
rule.Resources = groupResourceMapping[g]
|
||||
|
|
|
@ -684,14 +684,17 @@ func TestAddSpecialVerb(t *testing.T) {
|
|||
testCases := map[string]struct {
|
||||
verb string
|
||||
resource schema.GroupResource
|
||||
isNew bool
|
||||
}{
|
||||
"existing verb": {
|
||||
verb: "use",
|
||||
resource: schema.GroupResource{Group: "my.custom.io", Resource: "one"},
|
||||
isNew: false,
|
||||
},
|
||||
"new verb": {
|
||||
verb: "new",
|
||||
resource: schema.GroupResource{Group: "my.custom.io", Resource: "two"},
|
||||
isNew: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -703,6 +706,16 @@ func TestAddSpecialVerb(t *testing.T) {
|
|||
t.Errorf("missing expected verb: %s", tc.verb)
|
||||
}
|
||||
|
||||
if tc.isNew {
|
||||
if len(resources) != 1 {
|
||||
t.Errorf("new verb should only contain one resource resources:%#v", resources)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.resource, resources[0]) {
|
||||
t.Errorf("miss expected resource:%#v", tc.resource)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for _, res := range resources {
|
||||
if reflect.DeepEqual(tc.resource, res) {
|
||||
return
|
||||
|
|
|
@ -98,14 +98,10 @@ var (
|
|||
`)
|
||||
)
|
||||
|
||||
func boundObjectKindToAPIVersions() map[string]string {
|
||||
kinds := map[string]string{
|
||||
"Pod": "v1",
|
||||
"Secret": "v1",
|
||||
"Node": "v1",
|
||||
}
|
||||
|
||||
return kinds
|
||||
var boundObjectKinds = map[string]string{
|
||||
"Pod": "v1",
|
||||
"Secret": "v1",
|
||||
"Node": "v1",
|
||||
}
|
||||
|
||||
func NewTokenOpts(ioStreams genericiooptions.IOStreams) *TokenOptions {
|
||||
|
@ -149,7 +145,7 @@ func NewCmdCreateToken(f cmdutil.Factory, ioStreams genericiooptions.IOStreams)
|
|||
cmd.Flags().DurationVar(&o.Duration, "duration", o.Duration, "Requested lifetime of the issued token. If not set or if set to 0, the lifetime will be determined by the server automatically. The server may return a token with a longer or shorter lifetime.")
|
||||
|
||||
cmd.Flags().StringVar(&o.BoundObjectKind, "bound-object-kind", o.BoundObjectKind, "Kind of an object to bind the token to. "+
|
||||
"Supported kinds are "+strings.Join(sets.StringKeySet(boundObjectKindToAPIVersions()).List(), ", ")+". "+
|
||||
"Supported kinds are "+strings.Join(sets.List(sets.KeySet(boundObjectKinds)), ", ")+". "+
|
||||
"If set, --bound-object-name must be provided.")
|
||||
cmd.Flags().StringVar(&o.BoundObjectName, "bound-object-name", o.BoundObjectName, "Name of an object to bind the token to. "+
|
||||
"The token will expire when the object is deleted. "+
|
||||
|
@ -226,8 +222,8 @@ func (o *TokenOptions) Validate() error {
|
|||
return fmt.Errorf("--bound-object-uid can only be set if --bound-object-kind is provided")
|
||||
}
|
||||
} else {
|
||||
if _, ok := boundObjectKindToAPIVersions()[o.BoundObjectKind]; !ok {
|
||||
return fmt.Errorf("supported --bound-object-kind values are %s", strings.Join(sets.StringKeySet(boundObjectKindToAPIVersions()).List(), ", "))
|
||||
if _, ok := boundObjectKinds[o.BoundObjectKind]; !ok {
|
||||
return fmt.Errorf("supported --bound-object-kind values are %s", strings.Join(sets.List(sets.KeySet(boundObjectKinds)), ", "))
|
||||
}
|
||||
if len(o.BoundObjectName) == 0 {
|
||||
return fmt.Errorf("--bound-object-name is required if --bound-object-kind is provided")
|
||||
|
@ -250,7 +246,7 @@ func (o *TokenOptions) Run() error {
|
|||
if len(o.BoundObjectKind) > 0 {
|
||||
request.Spec.BoundObjectRef = &authenticationv1.BoundObjectReference{
|
||||
Kind: o.BoundObjectKind,
|
||||
APIVersion: boundObjectKindToAPIVersions()[o.BoundObjectKind],
|
||||
APIVersion: boundObjectKinds[o.BoundObjectKind],
|
||||
Name: o.BoundObjectName,
|
||||
UID: types.UID(o.BoundObjectUID),
|
||||
}
|
||||
|
|
|
@ -99,8 +99,7 @@ func TestCreateToken(t *testing.T) {
|
|||
serverResponseToken: "abc",
|
||||
expectStdout: `apiVersion: authentication.k8s.io/v1
|
||||
kind: TokenRequest
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
metadata: {}
|
||||
spec:
|
||||
audiences: null
|
||||
boundObjectRef: null
|
||||
|
|
|
@ -75,6 +75,9 @@ var (
|
|||
debugging utilities without restarting the pod.
|
||||
* Node: Create a new pod that runs in the node's host namespaces and can access
|
||||
the node's filesystem.
|
||||
|
||||
Note: When a non-root user is configured for the entire target Pod, some capabilities granted
|
||||
by debug profile may not work.
|
||||
`))
|
||||
|
||||
debugExample = templates.Examples(i18n.T(`
|
||||
|
@ -495,6 +498,8 @@ func (o *DebugOptions) debugByEphemeralContainer(ctx context.Context, pod *corev
|
|||
}
|
||||
klog.V(2).Infof("new ephemeral container: %#v", debugContainer)
|
||||
|
||||
o.displayWarning((*corev1.Container)(&debugContainer.EphemeralContainerCommon), pod)
|
||||
|
||||
debugJS, err := json.Marshal(debugPod)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error creating JSON for debug container: %v", err)
|
||||
|
@ -611,6 +616,16 @@ func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev
|
|||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var debugContainer *corev1.Container
|
||||
for i := range copied.Spec.Containers {
|
||||
if copied.Spec.Containers[i].Name == dc {
|
||||
debugContainer = &copied.Spec.Containers[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
o.displayWarning(debugContainer, copied)
|
||||
|
||||
created, err := o.podClient.Pods(copied.Namespace).Create(ctx, copied, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
@ -624,6 +639,32 @@ func (o *DebugOptions) debugByCopy(ctx context.Context, pod *corev1.Pod) (*corev
|
|||
return created, dc, nil
|
||||
}
|
||||
|
||||
// Display warning message if some capabilities are set by profile and non-root user is specified in .Spec.SecurityContext.RunAsUser.(#1650)
|
||||
func (o *DebugOptions) displayWarning(container *corev1.Container, pod *corev1.Pod) {
|
||||
if container == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if pod.Spec.SecurityContext.RunAsUser == nil || *pod.Spec.SecurityContext.RunAsUser == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if container.SecurityContext == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if (container.SecurityContext.Privileged == nil || !*container.SecurityContext.Privileged) &&
|
||||
(container.SecurityContext.Capabilities == nil || len(container.SecurityContext.Capabilities.Add) == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(o.ErrOut, `Warning: Non-root user is configured for the entire target Pod, and some capabilities granted by debug profile may not work. Please consider using "--custom" with a custom profile that specifies "securityContext.runAsUser: 0".`)
|
||||
}
|
||||
|
||||
// generateDebugContainer returns a debugging pod and an EphemeralContainer suitable for use as a debug container
|
||||
// in the given pod.
|
||||
func (o *DebugOptions) generateDebugContainer(pod *corev1.Pod) (*corev1.Pod, *corev1.EphemeralContainer, error) {
|
||||
|
|
|
@ -524,7 +524,7 @@ func (o *DeleteOptions) PrintObj(info *resource.Info) {
|
|||
}
|
||||
|
||||
func (o *DeleteOptions) confirmation(infos []*resource.Info) bool {
|
||||
fmt.Fprintf(o.Out, i18n.T("You are about to delete the following %d resource(s):\n"), len(infos))
|
||||
fmt.Fprintf(o.Out, i18n.T("You are about to delete the following %d resource(s):\n"), len(infos)) //nolint:errcheck
|
||||
for _, info := range infos {
|
||||
groupKind := info.Mapping.GroupVersionKind
|
||||
kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
|
||||
|
@ -532,11 +532,11 @@ func (o *DeleteOptions) confirmation(infos []*resource.Info) bool {
|
|||
kindString = strings.ToLower(groupKind.Kind)
|
||||
}
|
||||
|
||||
fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
|
||||
fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name) //nolint:errcheck
|
||||
}
|
||||
fmt.Fprint(o.Out, i18n.T("Do you want to continue?")+" (y/n): ")
|
||||
fmt.Fprint(o.Out, i18n.T("Do you want to continue?")+" (y/N): ") //nolint:errcheck
|
||||
var input string
|
||||
_, err := fmt.Fscan(o.In, &input)
|
||||
_, err := fmt.Fscanln(o.In, &input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -375,7 +375,7 @@ func TestDeleteObjectWithInteractive(t *testing.T) {
|
|||
}
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "You are about to delete the following 1 resource(s):\nreplicationcontroller/redis-master\nDo you want to continue? (y/n): replicationcontroller/redis-master\n" {
|
||||
if buf.String() != "You are about to delete the following 1 resource(s):\nreplicationcontroller/redis-master\nDo you want to continue? (y/N): replicationcontroller/redis-master\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
|
@ -396,7 +396,7 @@ func TestDeleteObjectWithInteractive(t *testing.T) {
|
|||
}
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "You are about to delete the following 1 resource(s):\nreplicationcontroller/redis-master\nDo you want to continue? (y/n): deletion is cancelled\n" {
|
||||
if buf.String() != "You are about to delete the following 1 resource(s):\nreplicationcontroller/redis-master\nDo you want to continue? (y/N): deletion is cancelled\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
if buf.String() == ": replicationcontroller/redis-master\n" {
|
||||
|
|
|
@ -193,7 +193,7 @@ func (o *DescribeOptions) Run() error {
|
|||
allErrs = append(allErrs, err)
|
||||
}
|
||||
|
||||
errs := sets.NewString()
|
||||
errs := sets.New[string]()
|
||||
first := true
|
||||
for _, info := range infos {
|
||||
mapping := info.ResourceMapping()
|
||||
|
|
|
@ -64,6 +64,7 @@ func TestDiffProgram(t *testing.T) {
|
|||
externalDiffCommands := [3]string{"diff", "diff -ruN", "diff --report-identical-files"}
|
||||
|
||||
t.Setenv("LANG", "C")
|
||||
t.Setenv("LANGUAGE", "en_US")
|
||||
|
||||
for i, c := range externalDiffCommands {
|
||||
t.Setenv("KUBECTL_EXTERNAL_DIFF", c)
|
||||
|
|
|
@ -326,7 +326,7 @@ func (o *DrainCmdOptions) RunDrain() error {
|
|||
return err
|
||||
}
|
||||
|
||||
drainedNodes := sets.NewString()
|
||||
drainedNodes := sets.New[string]()
|
||||
var fatal []error
|
||||
|
||||
remainingNodes := []string{}
|
||||
|
|
|
@ -172,7 +172,7 @@ func TestEdit(t *testing.T) {
|
|||
t.Setenv("KUBE_EDITOR", "testdata/test_editor.sh")
|
||||
t.Setenv("KUBE_EDITOR_CALLBACK", server.URL+"/callback")
|
||||
|
||||
testcases := sets.NewString()
|
||||
testcases := sets.New[string]()
|
||||
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -194,7 +194,7 @@ func TestEdit(t *testing.T) {
|
|||
t.Fatalf("Error locating edit testcases")
|
||||
}
|
||||
|
||||
for _, testcaseName := range testcases.List() {
|
||||
for _, testcaseName := range testcases.UnsortedList() {
|
||||
t.Run(testcaseName, func(t *testing.T) {
|
||||
i = 0
|
||||
name = testcaseName
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"kind": "Service",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"creationTimestamp\":null,\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"creationTimestamp": "2017-02-27T19:40:53Z",
|
||||
"labels": {
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ spec:
|
|||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
|
|
|
@ -29,7 +29,6 @@ spec:
|
|||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"kind": "Service",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"creationTimestamp\":null,\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"creationTimestamp": "2017-02-27T19:40:53Z",
|
||||
"labels": {
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"kind": "Service",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"creationTimestamp\":null,\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
"kubectl.kubernetes.io/last-applied-configuration": "{\"kind\":\"Service\",\"apiVersion\":\"v1\",\"metadata\":{\"name\":\"svc1\",\"labels\":{\"app\":\"svc1\"}},\"spec\":{\"ports\":[{\"name\":\"80\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"
|
||||
},
|
||||
"creationTimestamp": "2017-02-27T19:40:53Z",
|
||||
"labels": {
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -7,7 +7,7 @@ kind: Service
|
|||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","creationTimestamp":null,"labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
{"kind":"Service","apiVersion":"v1","metadata":{"name":"svc1","labels":{"app":"svc1"}},"spec":{"ports":[{"name":"80","protocol":"TCP","port":80,"targetPort":80}],"selector":{"app":"svc1"},"type":"ClusterIP"},"status":{"loadBalancer":{}}}
|
||||
creationTimestamp: "2017-02-27T19:40:53Z"
|
||||
labels:
|
||||
app: svc1
|
||||
|
|
|
@ -188,7 +188,7 @@ func (flags *EventsFlags) ToOptions() (*EventsOptions, error) {
|
|||
}
|
||||
|
||||
if len(o.FilterTypes) > 0 {
|
||||
o.FilterTypes = sets.NewString(o.FilterTypes...).List()
|
||||
o.FilterTypes = sets.List(sets.New[string](o.FilterTypes...))
|
||||
}
|
||||
|
||||
var printer printers.ResourcePrinter
|
||||
|
|
|
@ -114,18 +114,26 @@ func NewCmdExec(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Co
|
|||
|
||||
// RemoteExecutor defines the interface accepted by the Exec command - provided for test stubbing
|
||||
type RemoteExecutor interface {
|
||||
// Execute supports executing remote command in a pod.
|
||||
Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error
|
||||
|
||||
// ExecuteWithContext, in contrast to Execute, supports stopping the remote command via context cancellation.
|
||||
ExecuteWithContext(ctx context.Context, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error
|
||||
}
|
||||
|
||||
// DefaultRemoteExecutor is the standard implementation of remote command execution
|
||||
type DefaultRemoteExecutor struct{}
|
||||
|
||||
func (*DefaultRemoteExecutor) Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
func (d *DefaultRemoteExecutor) Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
return d.ExecuteWithContext(context.Background(), url, config, stdin, stdout, stderr, tty, terminalSizeQueue)
|
||||
}
|
||||
|
||||
func (*DefaultRemoteExecutor) ExecuteWithContext(ctx context.Context, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
exec, err := createExecutor(url, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
|
||||
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
|
|
|
@ -18,6 +18,7 @@ package exec
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -45,6 +46,10 @@ type fakeRemoteExecutor struct {
|
|||
}
|
||||
|
||||
func (f *fakeRemoteExecutor) Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
return f.ExecuteWithContext(context.Background(), url, config, stdin, stdout, stderr, tty, terminalSizeQueue)
|
||||
}
|
||||
|
||||
func (f *fakeRemoteExecutor) ExecuteWithContext(ctx context.Context, url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||
f.url = url
|
||||
return f.execErr
|
||||
}
|
||||
|
|
|
@ -56,44 +56,77 @@ var (
|
|||
|
||||
# Get the documentation of a specific field of a resource
|
||||
kubectl explain pods.spec.containers
|
||||
|
||||
|
||||
# Get the documentation of resources in different format
|
||||
kubectl explain deployment --output=plaintext-openapiv2`))
|
||||
)
|
||||
|
||||
const (
|
||||
plaintextTemplateName = "plaintext"
|
||||
plaintextOpenAPIV2TemplateName = "plaintext-openapiv2"
|
||||
)
|
||||
|
||||
type ExplainOptions struct {
|
||||
genericiooptions.IOStreams
|
||||
|
||||
CmdParent string
|
||||
APIVersion string
|
||||
Recursive bool
|
||||
|
||||
args []string
|
||||
|
||||
Mapper meta.RESTMapper
|
||||
openAPIGetter openapi.OpenAPIResourcesGetter
|
||||
|
||||
// Name of the template to use with the openapiv3 template renderer.
|
||||
// ExplainFlags directly reflect the information that CLI is gathering via flags.
|
||||
// They will be converted to Options, which reflect the runtime requirements for
|
||||
// the command.
|
||||
type ExplainFlags struct {
|
||||
APIVersion string
|
||||
OutputFormat string
|
||||
Recursive bool
|
||||
|
||||
// Client capable of fetching openapi documents from the user's cluster
|
||||
OpenAPIV3Client openapiclient.Client
|
||||
genericiooptions.IOStreams
|
||||
}
|
||||
|
||||
func NewExplainOptions(parent string, streams genericiooptions.IOStreams) *ExplainOptions {
|
||||
return &ExplainOptions{
|
||||
IOStreams: streams,
|
||||
CmdParent: parent,
|
||||
// NewExplainFlags returns a default ExplainFlags
|
||||
func NewExplainFlags(streams genericiooptions.IOStreams) *ExplainFlags {
|
||||
return &ExplainFlags{
|
||||
OutputFormat: plaintextTemplateName,
|
||||
IOStreams: streams,
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlags registers flags for a cli
|
||||
func (flags *ExplainFlags) AddFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().BoolVar(&flags.Recursive, "recursive", flags.Recursive, "Print the fields of fields (Currently only 1 level deep)")
|
||||
cmd.Flags().StringVar(&flags.APIVersion, "api-version", flags.APIVersion, "Get different explanations for particular API version (API group/version)")
|
||||
cmd.Flags().StringVarP(&flags.OutputFormat, "output", "o", plaintextTemplateName, "Format in which to render the schema (plaintext, plaintext-openapiv2)")
|
||||
}
|
||||
|
||||
// ToOptions converts from CLI inputs to runtime input
|
||||
func (flags *ExplainFlags) ToOptions(f cmdutil.Factory, parent string, args []string) (*ExplainOptions, error) {
|
||||
mapper, err := f.ToRESTMapper()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only openapi v3 needs the discovery client.
|
||||
openAPIV3Client, err := f.OpenAPIV3Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &ExplainOptions{
|
||||
IOStreams: flags.IOStreams,
|
||||
|
||||
Recursive: flags.Recursive,
|
||||
APIVersion: flags.APIVersion,
|
||||
OutputFormat: flags.OutputFormat,
|
||||
|
||||
CmdParent: parent,
|
||||
args: args,
|
||||
|
||||
Mapper: mapper,
|
||||
openAPIGetter: f,
|
||||
|
||||
OpenAPIV3Client: openAPIV3Client,
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// NewCmdExplain returns a cobra command for swagger docs
|
||||
func NewCmdExplain(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
|
||||
o := NewExplainOptions(parent, streams)
|
||||
flags := NewExplainFlags(streams)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "explain TYPE [--recursive=FALSE|TRUE] [--api-version=api-version-group] [-o|--output=plaintext|plaintext-openapiv2]",
|
||||
|
@ -102,37 +135,34 @@ func NewCmdExplain(parent string, f cmdutil.Factory, streams genericiooptions.IO
|
|||
Long: explainLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
|
||||
Example: explainExamples,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(o.Complete(f, cmd, args))
|
||||
o, err := flags.ToOptions(f, parent, args)
|
||||
cmdutil.CheckErr(err)
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.Run())
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVar(&o.Recursive, "recursive", o.Recursive, "When true, print the name of all the fields recursively. Otherwise, print the available fields with their description.")
|
||||
cmd.Flags().StringVar(&o.APIVersion, "api-version", o.APIVersion, "Use given api-version (group/version) of the resource.")
|
||||
|
||||
// Only enable --output as a valid flag if the feature is enabled
|
||||
cmd.Flags().StringVarP(&o.OutputFormat, "output", "o", plaintextTemplateName, "Format in which to render the schema. Valid values are: (plaintext, plaintext-openapiv2).")
|
||||
flags.AddFlags(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *ExplainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
o.Mapper, err = f.ToRESTMapper()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
type ExplainOptions struct {
|
||||
genericiooptions.IOStreams
|
||||
|
||||
// Only openapi v3 needs the discovery client.
|
||||
o.OpenAPIV3Client, err = f.OpenAPIV3Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Recursive bool
|
||||
APIVersion string
|
||||
// Name of the template to use with the openapiv3 template renderer.
|
||||
OutputFormat string
|
||||
|
||||
// Lazy-load the OpenAPI V2 Resources, so they're not loaded when using OpenAPI V3.
|
||||
o.openAPIGetter = f
|
||||
o.args = args
|
||||
return nil
|
||||
CmdParent string
|
||||
args []string
|
||||
|
||||
Mapper meta.RESTMapper
|
||||
openAPIGetter openapi.OpenAPIResourcesGetter
|
||||
|
||||
// Client capable of fetching openapi documents from the user's cluster
|
||||
OpenAPIV3Client openapiclient.Client
|
||||
}
|
||||
|
||||
func (o *ExplainOptions) Validate() error {
|
||||
|
|
|
@ -57,9 +57,9 @@ func TestExplainInvalidArgs(t *testing.T) {
|
|||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
opts := explain.NewExplainOptions("kubectl", genericiooptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", tf, genericiooptions.NewTestIOStreamsDiscard())
|
||||
err := opts.Complete(tf, cmd, []string{})
|
||||
flags := explain.NewExplainFlags(genericiooptions.NewTestIOStreamsDiscard())
|
||||
|
||||
opts, err := flags.ToOptions(tf, "kubectl", []string{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ func TestExplainInvalidArgs(t *testing.T) {
|
|||
t.Error("unexpected non-error")
|
||||
}
|
||||
|
||||
err = opts.Complete(tf, cmd, []string{"resource1", "resource2"})
|
||||
opts, err = flags.ToOptions(tf, "kubectl", []string{"resource1", "resource2"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
@ -84,9 +84,9 @@ func TestExplainNotExistResource(t *testing.T) {
|
|||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
opts := explain.NewExplainOptions("kubectl", genericiooptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", tf, genericiooptions.NewTestIOStreamsDiscard())
|
||||
err := opts.Complete(tf, cmd, []string{"foo"})
|
||||
flags := explain.NewExplainFlags(genericiooptions.NewTestIOStreamsDiscard())
|
||||
|
||||
opts, err := flags.ToOptions(tf, "kubectl", []string{"foo"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
|
|
@ -694,7 +694,6 @@ func TestExposeOverride(t *testing.T) {
|
|||
expected: `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
svc: test
|
||||
name: foo
|
||||
|
@ -717,7 +716,6 @@ status:
|
|||
expected: `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
svc: test
|
||||
name: foo
|
||||
|
@ -745,7 +743,6 @@ status:
|
|||
expected: `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
svc: test
|
||||
name: foo
|
||||
|
@ -773,7 +770,6 @@ status:
|
|||
expected: `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
svc: test
|
||||
name: foo
|
||||
|
|
|
@ -486,7 +486,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, args []string) error {
|
|||
}
|
||||
|
||||
allErrs := []error{}
|
||||
errs := sets.NewString()
|
||||
errs := sets.New[string]()
|
||||
infos, err := r.Infos()
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, err)
|
||||
|
|
|
@ -1438,7 +1438,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"name": "foo",
|
||||
"namespace": "test",
|
||||
"resourceVersion": "10"
|
||||
|
@ -1457,7 +1456,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"name": "bar",
|
||||
"namespace": "test",
|
||||
"resourceVersion": "11"
|
||||
|
@ -1476,7 +1474,6 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"name": "baz",
|
||||
"namespace": "test",
|
||||
"resourceVersion": "12"
|
||||
|
@ -2367,10 +2364,10 @@ DELETED test pod/foo 0/0 0 <unknown> <none>
|
|||
},
|
||||
{
|
||||
format: "json",
|
||||
expected: `{"type":"ADDED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"name":"bar","namespace":"test","resourceVersion":"9"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"ADDED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"name":"foo","namespace":"test","resourceVersion":"10"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"MODIFIED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"name":"foo","namespace":"test","resourceVersion":"11"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"DELETED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":null,"name":"foo","namespace":"test","resourceVersion":"12"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
expected: `{"type":"ADDED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"name":"bar","namespace":"test","resourceVersion":"9"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"ADDED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"name":"foo","namespace":"test","resourceVersion":"10"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"MODIFIED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"name":"foo","namespace":"test","resourceVersion":"11"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
{"type":"DELETED","object":{"apiVersion":"v1","kind":"Pod","metadata":{"name":"foo","namespace":"test","resourceVersion":"12"},"spec":{"containers":null,"dnsPolicy":"ClusterFirst","enableServiceLinks":true,"restartPolicy":"Always","securityContext":{},"terminationGracePeriodSeconds":30},"status":{}}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -2379,7 +2376,6 @@ DELETED test pod/foo 0/0 0 <unknown> <none>
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: bar
|
||||
namespace: test
|
||||
resourceVersion: "9"
|
||||
|
@ -2397,7 +2393,6 @@ object:
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: foo
|
||||
namespace: test
|
||||
resourceVersion: "10"
|
||||
|
@ -2415,7 +2410,6 @@ object:
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: foo
|
||||
namespace: test
|
||||
resourceVersion: "11"
|
||||
|
@ -2433,7 +2427,6 @@ object:
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: foo
|
||||
namespace: test
|
||||
resourceVersion: "12"
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v4"
|
||||
"k8s.io/klog/v2"
|
||||
|
@ -137,7 +136,7 @@ func NewCmdPatch(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra
|
|||
|
||||
cmd.Flags().StringVarP(&o.Patch, "patch", "p", "", "The patch to be applied to the resource JSON file.")
|
||||
cmd.Flags().StringVar(&o.PatchFile, "patch-file", "", "A file containing a patch to be applied to the resource.")
|
||||
cmd.Flags().StringVar(&o.PatchType, "type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List()))
|
||||
cmd.Flags().StringVar(&o.PatchType, "type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.List(sets.KeySet(patchTypes))))
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to update")
|
||||
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, patch will operate on the content of the file, not the server-side resource.")
|
||||
|
@ -194,7 +193,7 @@ func (o *PatchOptions) Validate() error {
|
|||
}
|
||||
if len(o.PatchType) != 0 {
|
||||
if _, ok := patchTypes[strings.ToLower(o.PatchType)]; !ok {
|
||||
return fmt.Errorf("--type must be one of %v, not %q", sets.StringKeySet(patchTypes).List(), o.PatchType)
|
||||
return fmt.Errorf("--type must be one of %v, not %q", sets.List(sets.KeySet(patchTypes)), o.PatchType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -260,7 +259,7 @@ func (o *PatchOptions) RunPatch() error {
|
|||
patchedObj, err := helper.Patch(namespace, name, patchType, patchBytes, nil)
|
||||
if err != nil {
|
||||
if apierrors.IsUnsupportedMediaType(err) {
|
||||
return errors.Wrap(err, fmt.Sprintf("%s is not supported by %s", patchType, mapping.GroupVersionKind))
|
||||
return fmt.Errorf("%s is not supported by %s: %w", patchType, mapping.GroupVersionKind, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ func convertPodNamedPortToNumber(ports []string, pod corev1.Pod) ([]string, erro
|
|||
return converted, nil
|
||||
}
|
||||
|
||||
func checkUDPPorts(udpOnlyPorts sets.Int, ports []string, obj metav1.Object) error {
|
||||
func checkUDPPorts(udpOnlyPorts sets.Set[int], ports []string, obj metav1.Object) error {
|
||||
for _, port := range ports {
|
||||
_, remotePort := splitPort(port)
|
||||
portNum, err := strconv.Atoi(remotePort)
|
||||
|
@ -281,8 +281,8 @@ func checkUDPPorts(udpOnlyPorts sets.Int, ports []string, obj metav1.Object) err
|
|||
// checkUDPPortInService returns an error if remote port in Service is a UDP port
|
||||
// TODO: remove this check after #47862 is solved
|
||||
func checkUDPPortInService(ports []string, svc *corev1.Service) error {
|
||||
udpPorts := sets.NewInt()
|
||||
tcpPorts := sets.NewInt()
|
||||
udpPorts := sets.New[int]()
|
||||
tcpPorts := sets.New[int]()
|
||||
for _, port := range svc.Spec.Ports {
|
||||
portNum := int(port.Port)
|
||||
switch port.Protocol {
|
||||
|
@ -298,8 +298,8 @@ func checkUDPPortInService(ports []string, svc *corev1.Service) error {
|
|||
// checkUDPPortInPod returns an error if remote port in Pod is a UDP port
|
||||
// TODO: remove this check after #47862 is solved
|
||||
func checkUDPPortInPod(ports []string, pod *corev1.Pod) error {
|
||||
udpPorts := sets.NewInt()
|
||||
tcpPorts := sets.NewInt()
|
||||
udpPorts := sets.New[int]()
|
||||
tcpPorts := sets.New[int]()
|
||||
for _, ct := range pod.Spec.Containers {
|
||||
for _, ctPort := range ct.Ports {
|
||||
portNum := int(ctPort.ContainerPort)
|
||||
|
|
|
@ -105,7 +105,7 @@ func NewCmdRolloutHistory(f cmdutil.Factory, streams genericiooptions.IOStreams)
|
|||
return cmd
|
||||
}
|
||||
|
||||
// Complete completes al the required options
|
||||
// Complete completes all the required options
|
||||
func (o *RolloutHistoryOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
o.Resources = args
|
||||
|
||||
|
@ -177,7 +177,15 @@ func (o *RolloutHistoryOptions) Run() error {
|
|||
}
|
||||
|
||||
if o.Revision > 0 {
|
||||
printer.PrintObj(historyInfo[o.Revision], o.Out)
|
||||
// Ensure the specified revision exists before printing
|
||||
revision, exists := historyInfo[o.Revision]
|
||||
if !exists {
|
||||
return fmt.Errorf("unable to find the specified revision")
|
||||
}
|
||||
|
||||
if err := printer.PrintObj(revision, o.Out); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
sortedKeys := make([]int64, 0, len(historyInfo))
|
||||
for k := range historyInfo {
|
||||
|
|
|
@ -280,15 +280,12 @@ func TestRolloutHistoryWithOutput(t *testing.T) {
|
|||
"kind": "ReplicaSet",
|
||||
"apiVersion": "apps/v1",
|
||||
"metadata": {
|
||||
"name": "rev2",
|
||||
"creationTimestamp": null
|
||||
"name": "rev2"
|
||||
},
|
||||
"spec": {
|
||||
"selector": null,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"creationTimestamp": null
|
||||
},
|
||||
"metadata": {},
|
||||
"spec": {
|
||||
"containers": null
|
||||
}
|
||||
|
@ -305,13 +302,11 @@ func TestRolloutHistoryWithOutput(t *testing.T) {
|
|||
expectedOutput: `apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: rev2
|
||||
spec:
|
||||
selector: null
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
metadata: {}
|
||||
spec:
|
||||
containers: null
|
||||
status:
|
||||
|
@ -323,13 +318,11 @@ status:
|
|||
expectedOutput: `apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: rev1
|
||||
spec:
|
||||
selector: null
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
metadata: {}
|
||||
spec:
|
||||
containers: null
|
||||
status:
|
||||
|
@ -338,13 +331,11 @@ status:
|
|||
apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: rev2
|
||||
spec:
|
||||
selector: null
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
metadata: {}
|
||||
spec:
|
||||
containers: null
|
||||
status:
|
||||
|
@ -401,6 +392,88 @@ replicaset.apps/rev2
|
|||
}
|
||||
}
|
||||
|
||||
func TestRolloutHistoryErrors(t *testing.T) {
|
||||
ns := scheme.Codecs.WithoutConversion()
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||
encoder := ns.EncoderForVersion(info.Serializer, rolloutPauseGroupVersionEncoder)
|
||||
|
||||
tf.Client = &RolloutPauseRESTClient{
|
||||
RESTClient: &fake.RESTClient{
|
||||
GroupVersion: rolloutPauseGroupVersionEncoder,
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/deployments/foo" && m == "GET":
|
||||
responseDeployment := &appsv1.Deployment{}
|
||||
responseDeployment.Name = "foo"
|
||||
body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployment))))
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
revision int64
|
||||
outputFormat string
|
||||
expectedError string
|
||||
}{
|
||||
"get non-existing revision as yaml": {
|
||||
revision: 999,
|
||||
outputFormat: "yaml",
|
||||
expectedError: "unable to find the specified revision",
|
||||
},
|
||||
"get non-existing revision as json": {
|
||||
revision: 999,
|
||||
outputFormat: "json",
|
||||
expectedError: "unable to find the specified revision",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
fhv := setupFakeHistoryViewer(t)
|
||||
fhv.getHistoryFn = func(namespace, name string) (map[int64]runtime.Object, error) {
|
||||
return map[int64]runtime.Object{
|
||||
1: &appsv1.ReplicaSet{ObjectMeta: v1.ObjectMeta{Name: "rev1"}},
|
||||
2: &appsv1.ReplicaSet{ObjectMeta: v1.ObjectMeta{Name: "rev2"}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
streams := genericiooptions.NewTestIOStreamsDiscard()
|
||||
o := NewRolloutHistoryOptions(streams)
|
||||
|
||||
printFlags := &genericclioptions.PrintFlags{
|
||||
JSONYamlPrintFlags: &genericclioptions.JSONYamlPrintFlags{
|
||||
ShowManagedFields: true,
|
||||
},
|
||||
OutputFormat: &tc.outputFormat,
|
||||
OutputFlagSpecified: func() bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
o.PrintFlags = printFlags
|
||||
o.Revision = tc.revision
|
||||
|
||||
if err := o.Complete(tf, nil, []string{"deployment/foo"}); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err := o.Run()
|
||||
if err != nil && err.Error() != tc.expectedError {
|
||||
t.Fatalf("expected '%s' error, but got: %v", tc.expectedError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
opts := RolloutHistoryOptions{
|
||||
Revision: 0,
|
||||
|
|
|
@ -655,7 +655,6 @@ func TestRunOverride(t *testing.T) {
|
|||
expectedOutput: `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
run: test
|
||||
name: test
|
||||
|
@ -678,7 +677,6 @@ status: {}
|
|||
expectedOutput: `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
run: test
|
||||
name: test
|
||||
|
@ -701,7 +699,6 @@ status: {}
|
|||
expectedOutput: `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
run: test
|
||||
name: test
|
||||
|
@ -729,7 +726,6 @@ status: {}
|
|||
expectedOutput: `apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
foo: bar
|
||||
run: test
|
||||
|
|
|
@ -15,4 +15,4 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
// Package env provides functions to incorporate environment variables into set env.
|
||||
package env // import "k8s.io/kubectl/pkg/cmd/set/env"
|
||||
package env
|
||||
|
|
|
@ -61,7 +61,7 @@ func SplitEnvironmentFromResources(args []string) (resources, envArgs []string,
|
|||
// envVarType is for making errors more specific to user intentions.
|
||||
func parseIntoEnvVar(spec []string, defaultReader io.Reader, envVarType string) ([]v1.EnvVar, []string, bool, error) {
|
||||
env := []v1.EnvVar{}
|
||||
exists := sets.NewString()
|
||||
exists := sets.New[string]()
|
||||
var remove []string
|
||||
usedStdin := false
|
||||
for _, envSpec := range spec {
|
||||
|
|
|
@ -158,11 +158,11 @@ func splitMaybeSubscriptedPath(fieldPath string) (string, string, bool) {
|
|||
// formatMap formats map[string]string to a string.
|
||||
func formatMap(m map[string]string) (fmtStr string) {
|
||||
// output with keys in sorted order to provide stable output
|
||||
keys := sets.NewString()
|
||||
keys := sets.New[string]()
|
||||
for key := range m {
|
||||
keys.Insert(key)
|
||||
}
|
||||
for _, key := range keys.List() {
|
||||
for _, key := range sets.List(keys) {
|
||||
fmtStr += fmt.Sprintf("%v=%q\n", key, m[key])
|
||||
}
|
||||
fmtStr = strings.TrimSuffix(fmtStr, "\n")
|
||||
|
|
|
@ -19,7 +19,7 @@ package set
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
|
@ -139,7 +139,7 @@ func findEnv(env []v1.EnvVar, name string) (v1.EnvVar, bool) {
|
|||
// If a variable is both added and removed, the removal takes precedence.
|
||||
func updateEnv(existing []v1.EnvVar, env []v1.EnvVar, remove []string) []v1.EnvVar {
|
||||
out := []v1.EnvVar{}
|
||||
covered := sets.NewString(remove...)
|
||||
covered := sets.New[string](remove...)
|
||||
for _, e := range existing {
|
||||
if covered.Has(e.Name) {
|
||||
continue
|
||||
|
|
|
@ -206,7 +206,7 @@ func (o *SubjectOptions) Validate() error {
|
|||
func (o *SubjectOptions) Run(fn updateSubjects) error {
|
||||
patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {
|
||||
subjects := []rbacv1.Subject{}
|
||||
for _, user := range sets.NewString(o.Users...).List() {
|
||||
for _, user := range sets.List(sets.New[string](o.Users...)) {
|
||||
subject := rbacv1.Subject{
|
||||
Kind: rbacv1.UserKind,
|
||||
APIGroup: rbacv1.GroupName,
|
||||
|
@ -214,7 +214,7 @@ func (o *SubjectOptions) Run(fn updateSubjects) error {
|
|||
}
|
||||
subjects = append(subjects, subject)
|
||||
}
|
||||
for _, group := range sets.NewString(o.Groups...).List() {
|
||||
for _, group := range sets.List(sets.New[string](o.Groups...)) {
|
||||
subject := rbacv1.Subject{
|
||||
Kind: rbacv1.GroupKind,
|
||||
APIGroup: rbacv1.GroupName,
|
||||
|
@ -222,7 +222,7 @@ func (o *SubjectOptions) Run(fn updateSubjects) error {
|
|||
}
|
||||
subjects = append(subjects, subject)
|
||||
}
|
||||
for _, sa := range sets.NewString(o.ServiceAccounts...).List() {
|
||||
for _, sa := range sets.List(sets.New[string](o.ServiceAccounts...)) {
|
||||
tokens := strings.Split(sa, ":")
|
||||
namespace := tokens[0]
|
||||
name := tokens[1]
|
||||
|
|
|
@ -38,7 +38,7 @@ const (
|
|||
// It also validates the spec. For example, the form `<key>` may be used to remove a taint, but not to add one.
|
||||
func parseTaints(spec []string) ([]corev1.Taint, []corev1.Taint, error) {
|
||||
var taints, taintsToRemove []corev1.Taint
|
||||
uniqueTaints := map[corev1.TaintEffect]sets.String{}
|
||||
uniqueTaints := map[corev1.TaintEffect]sets.Set[string]{}
|
||||
|
||||
for _, taintSpec := range spec {
|
||||
if strings.HasSuffix(taintSpec, "-") {
|
||||
|
@ -62,7 +62,7 @@ func parseTaints(spec []string) ([]corev1.Taint, []corev1.Taint, error) {
|
|||
}
|
||||
// add taint to existingTaints for uniqueness check
|
||||
if len(uniqueTaints[newTaint.Effect]) == 0 {
|
||||
uniqueTaints[newTaint.Effect] = sets.String{}
|
||||
uniqueTaints[newTaint.Effect] = sets.Set[string]{}
|
||||
}
|
||||
uniqueTaints[newTaint.Effect].Insert(newTaint.Key)
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ package top
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||
|
@ -45,6 +45,7 @@ type TopNodeOptions struct {
|
|||
NoHeaders bool
|
||||
UseProtocolBuffers bool
|
||||
ShowCapacity bool
|
||||
ShowSwap bool
|
||||
|
||||
NodeClient corev1client.CoreV1Interface
|
||||
Printer *metricsutil.TopCmdPrinter
|
||||
|
@ -95,6 +96,7 @@ func NewCmdTopNode(f cmdutil.Factory, o *TopNodeOptions, streams genericiooption
|
|||
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers")
|
||||
cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.")
|
||||
cmd.Flags().BoolVar(&o.ShowCapacity, "show-capacity", o.ShowCapacity, "Print node resources based on Capacity instead of Allocatable(default) of the nodes.")
|
||||
cmd.Flags().BoolVar(&o.ShowSwap, "show-swap", o.ShowSwap, "Print node resources related to swap memory.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -127,7 +129,7 @@ func (o *TopNodeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
|
|||
|
||||
o.NodeClient = clientset.CoreV1()
|
||||
|
||||
o.Printer = metricsutil.NewTopCmdPrinter(o.Out)
|
||||
o.Printer = metricsutil.NewTopCmdPrinter(o.Out, o.ShowSwap)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -198,6 +200,14 @@ func (o TopNodeOptions) RunTopNode() error {
|
|||
} else {
|
||||
availableResources[n.Name] = n.Status.Capacity
|
||||
}
|
||||
|
||||
if n.Status.NodeInfo.Swap != nil && n.Status.NodeInfo.Swap.Capacity != nil {
|
||||
swapCapacity := *n.Status.NodeInfo.Swap.Capacity
|
||||
availableResources[n.Name]["swap"] = *resource.NewQuantity(swapCapacity, resource.BinarySI)
|
||||
} else {
|
||||
o.Printer.RegisterMissingResource(n.Name, metricsutil.ResourceSwap)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return o.Printer.PrintNodeMetrics(metrics.Items, availableResources, o.NoHeaders, o.SortBy)
|
||||
|
|
|
@ -424,3 +424,111 @@ func TestTopNodeWithSortByMemoryMetricsFrom(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTopNodeWithSwap(t *testing.T) {
|
||||
runTopCmd := func(expectedMetrics *metricsv1beta1api.NodeMetricsList, nodes *v1.NodeList) (result string) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
expectedNodePath := fmt.Sprintf("/%s/%s/nodes", apiPrefix, apiVersion)
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
ns := scheme.Codecs.WithoutConversion()
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/api":
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
|
||||
case p == "/apis":
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
|
||||
case p == expectedNodePath && m == "GET":
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, nodes)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\nGot URL: %#v\n", req, req.URL)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
fakemetricsClientset := &metricsfake.Clientset{}
|
||||
fakemetricsClientset.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, expectedMetrics, nil
|
||||
})
|
||||
tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
|
||||
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdTopNode(tf, nil, streams)
|
||||
|
||||
// TODO in the long run, we want to test most of our commands like this. Wire the options struct with specific mocks
|
||||
// TODO then check the particular Run functionality and harvest results from fake clients
|
||||
cmdOptions := &TopNodeOptions{
|
||||
IOStreams: streams,
|
||||
ShowSwap: true,
|
||||
}
|
||||
if err := cmdOptions.Complete(tf, cmd, []string{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cmdOptions.MetricsClient = fakemetricsClientset
|
||||
if err := cmdOptions.Validate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := cmdOptions.RunTopNode(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
isSwapDisabledOnNodes bool
|
||||
}{
|
||||
{
|
||||
name: "nodes with swap",
|
||||
isSwapDisabledOnNodes: false,
|
||||
},
|
||||
{
|
||||
name: "nodes without swap",
|
||||
isSwapDisabledOnNodes: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
expectedMetrics, nodes := testNodeV1beta1MetricsData()
|
||||
if tc.isSwapDisabledOnNodes {
|
||||
for i := range expectedMetrics.Items {
|
||||
delete(expectedMetrics.Items[i].Usage, "swap")
|
||||
}
|
||||
for i := range nodes.Items {
|
||||
nodes.Items[i].Status.NodeInfo.Swap = nil
|
||||
}
|
||||
}
|
||||
|
||||
result := runTopCmd(expectedMetrics, nodes)
|
||||
fmt.Printf("%s\n", result)
|
||||
|
||||
if !strings.Contains(result, "SWAP(bytes)") {
|
||||
t.Errorf("missing SWAP(bytes) header: \n%s", result)
|
||||
}
|
||||
if !strings.Contains(result, "SWAP(%)") {
|
||||
t.Errorf("missing SWAP(%%) header: \n%s", result)
|
||||
}
|
||||
|
||||
if tc.isSwapDisabledOnNodes {
|
||||
if !strings.Contains(result, "<unknown>") {
|
||||
t.Errorf("expected swap to be <unknown>: \n%s", result)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range expectedMetrics.Items {
|
||||
if !strings.Contains(result, m.Name) {
|
||||
t.Errorf("missing metrics for %s: \n%s", m.Name, result)
|
||||
}
|
||||
if _, foundSwapMetric := m.Usage["swap"]; foundSwapMetric != !tc.isSwapDisabledOnNodes {
|
||||
t.Errorf("missing swap metric for %s: \n%s", m.Name, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ type TopPodOptions struct {
|
|||
NoHeaders bool
|
||||
UseProtocolBuffers bool
|
||||
Sum bool
|
||||
ShowSwap bool
|
||||
|
||||
PodClient corev1client.PodsGetter
|
||||
Printer *metricsutil.TopCmdPrinter
|
||||
|
@ -117,6 +118,7 @@ func NewCmdTopPod(f cmdutil.Factory, o *TopPodOptions, streams genericiooptions.
|
|||
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers.")
|
||||
cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.")
|
||||
cmd.Flags().BoolVar(&o.Sum, "sum", o.Sum, "Print the sum of the resource usage")
|
||||
cmd.Flags().BoolVar(&o.ShowSwap, "show-swap", o.ShowSwap, "Print pod resources related to swap memory.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -152,7 +154,7 @@ func (o *TopPodOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []s
|
|||
|
||||
o.PodClient = clientset.CoreV1()
|
||||
|
||||
o.Printer = metricsutil.NewTopCmdPrinter(o.Out)
|
||||
o.Printer = metricsutil.NewTopCmdPrinter(o.Out, o.ShowSwap)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,12 @@ func TestTopPod(t *testing.T) {
|
|||
namespaces: []string{testNS, testNS, testNS},
|
||||
containers: true,
|
||||
},
|
||||
{
|
||||
name: "with swap",
|
||||
options: &TopPodOptions{AllNamespaces: true, ShowSwap: true},
|
||||
namespaces: []string{testNS, "secondtestns", "thirdtestns"},
|
||||
listsNamespaces: true,
|
||||
},
|
||||
}
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
for _, testCase := range testCases {
|
||||
|
@ -284,10 +290,101 @@ func TestTopPod(t *testing.T) {
|
|||
t.Errorf("containers not matching:\n\texpectedContainers: %v\n\tresultContainers: %v\n", testCase.expectedContainers, resultContainers)
|
||||
}
|
||||
}
|
||||
if testCase.options != nil && testCase.options.ShowSwap {
|
||||
if !strings.Contains(result, "SWAP(bytes)") {
|
||||
t.Errorf("missing SWAP(bytes) header: \n%s", result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopPodWithSwap(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
|
||||
const testName = "TestTopPodWithSwap"
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
metricsList := testV1beta1PodMetricsData()
|
||||
fakemetricsClientset := &metricsfake.Clientset{}
|
||||
|
||||
fakemetricsClientset.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
res := &metricsv1beta1api.PodMetricsList{
|
||||
Items: metricsList,
|
||||
}
|
||||
return true, res, nil
|
||||
})
|
||||
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
ns := scheme.Codecs.WithoutConversion()
|
||||
|
||||
tf.Client = &fake.RESTClient{
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/api":
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apibody)))}, nil
|
||||
case "/apis":
|
||||
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: io.NopCloser(bytes.NewReader([]byte(apisbodyWithMetrics)))}, nil
|
||||
default:
|
||||
t.Fatalf("%s: unexpected request: %#v\nGot URL: %#v",
|
||||
testName, req, req.URL)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdTopPod(tf, nil, streams)
|
||||
cmdOptions := &TopPodOptions{
|
||||
ShowSwap: true,
|
||||
}
|
||||
cmdOptions.IOStreams = streams
|
||||
|
||||
if err := cmdOptions.Complete(tf, cmd, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cmdOptions.MetricsClient = fakemetricsClientset
|
||||
if err := cmdOptions.Validate(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := cmdOptions.RunTopPod(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result := buf.String()
|
||||
|
||||
expectedSwapBytes := map[string]string{
|
||||
"pod1": "4Mi",
|
||||
"pod2": "0Mi",
|
||||
"pod3": "3Mi",
|
||||
}
|
||||
|
||||
actualSwapBytes := map[string]string{}
|
||||
for _, line := range strings.Split(result, "\n")[1:] {
|
||||
lineFields := strings.Fields(line)
|
||||
if len(lineFields) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
podName := lineFields[0]
|
||||
swapBytes := lineFields[3]
|
||||
actualSwapBytes[podName] = swapBytes
|
||||
}
|
||||
|
||||
for expectedPodName, expectedSwapBytes := range expectedSwapBytes {
|
||||
actualSwapBytes, found := actualSwapBytes[expectedPodName]
|
||||
if !found {
|
||||
t.Errorf("missing swap metrics for pod %s", expectedPodName)
|
||||
}
|
||||
if actualSwapBytes != expectedSwapBytes {
|
||||
t.Errorf("unexpected swap metrics for pod %s: expected %s, got %s", expectedPodName, expectedSwapBytes, actualSwapBytes)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getResultColumnValues(result string, columnIndex int) []string {
|
||||
resultLines := strings.Split(result, "\n")
|
||||
values := make([]string, len(resultLines)-2) // don't process first (header) and last (empty) line
|
||||
|
@ -410,6 +507,7 @@ func testV1beta1PodMetricsData() []metricsv1beta1api.PodMetrics {
|
|||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(2*(1024*1024), resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(1*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
|
@ -418,6 +516,7 @@ func testV1beta1PodMetricsData() []metricsv1beta1api.PodMetrics {
|
|||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(4, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(5*(1024*1024), resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(6*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
|
@ -462,6 +561,7 @@ func testV1beta1PodMetricsData() []metricsv1beta1api.PodMetrics {
|
|||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(7, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(8*(1024*1024), resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(9*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
metricsv1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func TestTopSubcommandsExist(t *testing.T) {
|
||||
|
@ -63,6 +64,7 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
Window: metav1.Duration{Duration: time.Minute},
|
||||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(1, resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(1*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(2*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(3*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
|
@ -72,6 +74,7 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
Window: metav1.Duration{Duration: time.Minute},
|
||||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(5, resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(2*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(6*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(7*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
|
@ -81,6 +84,7 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
Window: metav1.Duration{Duration: time.Minute},
|
||||
Usage: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(3, resource.DecimalSI),
|
||||
"swap": *resource.NewQuantity(0, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(4*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(5*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
|
@ -100,6 +104,11 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
v1.ResourceMemory: *resource.NewQuantity(20*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(30*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
Swap: &v1.NodeSwapStatus{
|
||||
Capacity: ptr.To(int64(10 * (1024 * 1024 * 1024))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -110,6 +119,11 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
v1.ResourceMemory: *resource.NewQuantity(60*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(70*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
Swap: &v1.NodeSwapStatus{
|
||||
Capacity: ptr.To(int64(20 * (1024 * 1024 * 1024))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -120,6 +134,11 @@ func testNodeV1beta1MetricsData() (*metricsv1beta1api.NodeMetricsList, *v1.NodeL
|
|||
v1.ResourceMemory: *resource.NewQuantity(40*(1024*1024), resource.DecimalSI),
|
||||
v1.ResourceStorage: *resource.NewQuantity(50*(1024*1024), resource.DecimalSI),
|
||||
},
|
||||
NodeInfo: v1.NodeSystemInfo{
|
||||
Swap: &v1.NodeSwapStatus{
|
||||
Capacity: ptr.To(int64(30 * (1024 * 1024 * 1024))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -222,7 +222,7 @@ func checkErr(err error, handleErr func(string, int)) {
|
|||
|
||||
func statusCausesToAggrError(scs []metav1.StatusCause) utilerrors.Aggregate {
|
||||
errs := make([]error, 0, len(scs))
|
||||
errorMsgs := sets.NewString()
|
||||
errorMsgs := sets.New[string]()
|
||||
for _, sc := range scs {
|
||||
// check for duplicate error messages and skip them
|
||||
msg := fmt.Sprintf("%s: %s", sc.Field, sc.Message)
|
||||
|
@ -425,13 +425,36 @@ func GetPodRunningTimeoutFlag(cmd *cobra.Command) (time.Duration, error) {
|
|||
type FeatureGate string
|
||||
|
||||
const (
|
||||
ApplySet FeatureGate = "KUBECTL_APPLYSET"
|
||||
CmdPluginAsSubcommand FeatureGate = "KUBECTL_ENABLE_CMD_SHADOW"
|
||||
OpenAPIV3Patch FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
|
||||
// owner: @soltysh
|
||||
// kep: https://kep.k8s.io/859
|
||||
//
|
||||
// HTTP headers with command name and flags used.
|
||||
CmdHeaders FeatureGate = "KUBECTL_COMMAND_HEADERS"
|
||||
|
||||
// owner: @ardaguclu
|
||||
// kep: https://kep.k8s.io/3104
|
||||
//
|
||||
// Separate kubectl user preferences.
|
||||
KubeRC FeatureGate = "KUBECTL_KUBERC"
|
||||
|
||||
// owner: @soltysh
|
||||
// kep: https://kep.k8s.io/3515
|
||||
//
|
||||
// Improved kubectl apply --prune behavior.
|
||||
OpenAPIV3Patch FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
|
||||
|
||||
// owner: @justinb
|
||||
// kep: https://kep.k8s.io/3659
|
||||
//
|
||||
// Improved kubectl apply --prune behavior.
|
||||
ApplySet FeatureGate = "KUBECTL_APPLYSET"
|
||||
|
||||
// owner: @seans
|
||||
// kep: https://kep.k8s.io/4006
|
||||
//
|
||||
// Transition to WebSockets.
|
||||
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
|
||||
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
|
||||
// DebugCustomProfile should be dropped in 1.34
|
||||
DebugCustomProfile FeatureGate = "KUBECTL_DEBUG_CUSTOM_PROFILE"
|
||||
)
|
||||
|
||||
// IsEnabled returns true iff environment variable is set to true.
|
||||
|
@ -519,7 +542,7 @@ func AddChunkSizeFlag(cmd *cobra.Command, value *int64) {
|
|||
}
|
||||
|
||||
func AddLabelSelectorFlagVar(cmd *cobra.Command, p *string) {
|
||||
cmd.Flags().StringVarP(p, "selector", "l", *p, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.")
|
||||
cmd.Flags().StringVarP(p, "selector", "l", *p, "Selector (label query) to filter on, supports '=', '==', '!=', 'in', 'notin'.(e.g. -l key1=value1,key2=value2,key3 in (value3)). Matching objects must satisfy all of the specified label constraints.")
|
||||
}
|
||||
|
||||
func AddPruningFlags(cmd *cobra.Command, prune *bool, pruneAllowlist *[]string, all *bool, applySetRef *string) {
|
||||
|
@ -538,7 +561,7 @@ func AddPruningFlags(cmd *cobra.Command, prune *bool, pruneAllowlist *[]string,
|
|||
}
|
||||
|
||||
func AddSubresourceFlags(cmd *cobra.Command, subresource *string, usage string) {
|
||||
cmd.Flags().StringVar(subresource, "subresource", "", fmt.Sprintf("%s This flag is beta and may change in the future.", usage))
|
||||
cmd.Flags().StringVar(subresource, "subresource", "", usage)
|
||||
CheckErr(cmd.RegisterFlagCompletionFunc("subresource", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
|
||||
var commonSubresources = []string{"status", "scale", "resize"}
|
||||
return commonSubresources, cobra.ShellCompDirectiveNoFileComp
|
||||
|
|
|
@ -18,10 +18,10 @@ package version
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"math"
|
||||
)
|
||||
|
||||
// supportedMinorVersionSkew is the maximum supported difference between the client and server minor versions.
|
||||
|
@ -29,26 +29,26 @@ import (
|
|||
// and server versions 1.18, 1.19, and 1.20 would be within the supported version skew for client version 1.19.
|
||||
const supportedMinorVersionSkew = 1
|
||||
|
||||
// printVersionSkewWarning prints a warning message if the difference between the client and version is greater than
|
||||
// getVersionSkewWarning returns a warning message if the difference between the client and version is greater than
|
||||
// the supported version skew.
|
||||
func printVersionSkewWarning(w io.Writer, clientVersion, serverVersion apimachineryversion.Info) error {
|
||||
func getVersionSkewWarning(clientVersion, serverVersion apimachineryversion.Info) (string, error) {
|
||||
parsedClientVersion, err := version.ParseSemantic(clientVersion.GitVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", fmt.Errorf("client version error: %w", err)
|
||||
}
|
||||
|
||||
parsedServerVersion, err := version.ParseSemantic(serverVersion.GitVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", fmt.Errorf("server version error: %w", err)
|
||||
}
|
||||
|
||||
majorVersionDifference := math.Abs(float64(parsedClientVersion.Major()) - float64(parsedServerVersion.Major()))
|
||||
minorVersionDifference := math.Abs(float64(parsedClientVersion.Minor()) - float64(parsedServerVersion.Minor()))
|
||||
|
||||
if majorVersionDifference > 0 || minorVersionDifference > supportedMinorVersionSkew {
|
||||
fmt.Fprintf(w, "WARNING: version difference between client (%d.%d) and server (%d.%d) exceeds the supported minor version skew of +/-%d\n",
|
||||
warningMessage := fmt.Sprintf("version difference between client (%d.%d) and server (%d.%d) exceeds the supported minor version skew of +/-%d",
|
||||
parsedClientVersion.Major(), parsedClientVersion.Minor(), parsedServerVersion.Major(), parsedServerVersion.Minor(), supportedMinorVersionSkew)
|
||||
return warningMessage, nil
|
||||
}
|
||||
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -17,14 +17,12 @@ limitations under the License.
|
|||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
"testing"
|
||||
|
||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||
)
|
||||
|
||||
func TestPrintVersionSkewWarning(t *testing.T) {
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
clientVersion apimachineryversion.Info
|
||||
|
@ -82,14 +80,15 @@ func TestPrintVersionSkewWarning(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
output.Reset()
|
||||
warningMessage, err := getVersionSkewWarning(tc.clientVersion, tc.serverVersion)
|
||||
if err != nil {
|
||||
t.Errorf("error: %s", err)
|
||||
}
|
||||
|
||||
printVersionSkewWarning(output, tc.clientVersion, tc.serverVersion)
|
||||
|
||||
if tc.isWarningExpected && output.Len() == 0 {
|
||||
t.Error("warning was expected, but not written to the output")
|
||||
} else if !tc.isWarningExpected && output.Len() > 0 {
|
||||
t.Errorf("warning was not expected, but was written to the output: %s", output.String())
|
||||
if tc.isWarningExpected && warningMessage == "" {
|
||||
t.Error("warning was expected")
|
||||
} else if !tc.isWarningExpected && warningMessage != "" {
|
||||
t.Errorf("warning was not expected. but got %s", warningMessage)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -53,8 +53,9 @@ var (
|
|||
|
||||
// Options is a struct to support version command
|
||||
type Options struct {
|
||||
ClientOnly bool
|
||||
Output string
|
||||
ClientOnly bool
|
||||
Output string
|
||||
WarningsAsErrors bool
|
||||
|
||||
args []string
|
||||
|
||||
|
@ -93,6 +94,9 @@ func NewCmdVersion(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cob
|
|||
// Complete completes all the required options
|
||||
func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
o.WarningsAsErrors = cmd.Flags().Lookup("warnings-as-errors").Value.String() == "true"
|
||||
|
||||
if o.ClientOnly {
|
||||
return nil
|
||||
}
|
||||
|
@ -162,11 +166,17 @@ func (o *Options) Run() error {
|
|||
}
|
||||
|
||||
if versionInfo.ServerVersion != nil {
|
||||
if err := printVersionSkewWarning(o.ErrOut, *versionInfo.ClientVersion, *versionInfo.ServerVersion); err != nil {
|
||||
warningMessage, err := getVersionSkewWarning(*versionInfo.ClientVersion, *versionInfo.ServerVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if warningMessage != "" {
|
||||
if o.WarningsAsErrors {
|
||||
return errors.New(warningMessage)
|
||||
}
|
||||
fmt.Fprintf(o.ErrOut, "Warning: %s\n", warningMessage) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
return serverErr
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
|
@ -32,13 +30,18 @@ func TestNewCmdVersionClientVersion(t *testing.T) {
|
|||
defer tf.Cleanup()
|
||||
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||
o := NewOptions(streams)
|
||||
if err := o.Complete(tf, &cobra.Command{}, nil); err != nil {
|
||||
|
||||
cmd := NewCmdVersion(tf, streams)
|
||||
cmd.Flags().Bool("warnings-as-errors", false, "")
|
||||
|
||||
if err := o.Complete(tf, cmd, nil); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if err := o.Validate(); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if err := o.Complete(tf, &cobra.Command{}, []string{"extraParameter0"}); err != nil {
|
||||
|
||||
if err := o.Complete(tf, cmd, []string{"extraParameter0"}); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if err := o.Validate(); !strings.Contains(err.Error(), "extra arguments") {
|
||||
|
|
|
@ -329,7 +329,9 @@ func (o *WaitOptions) RunWait() error {
|
|||
// or functions from ResourceBuilder for parsing those. Lastly, this poll
|
||||
// should be replaced with a ListWatch cache.
|
||||
if err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, o.Timeout, true, func(context.Context) (done bool, err error) {
|
||||
foundResource := false
|
||||
visitErr := o.ResourceFinder.Do().Visit(func(info *resource.Info, err error) error {
|
||||
foundResource = true
|
||||
return nil
|
||||
})
|
||||
if apierrors.IsNotFound(visitErr) {
|
||||
|
@ -338,7 +340,7 @@ func (o *WaitOptions) RunWait() error {
|
|||
if visitErr != nil {
|
||||
return false, visitErr
|
||||
}
|
||||
return true, nil
|
||||
return foundResource, nil
|
||||
}); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return fmt.Errorf("%s", wait.ErrWaitTimeout.Error()) // nolint:staticcheck // SA1019
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
# Disable inheritance as this is an api owners file
|
||||
options:
|
||||
no_parent_owners: true
|
||||
approvers:
|
||||
- api-approvers
|
||||
reviewers:
|
||||
- api-reviewers
|
||||
- sig-cli-reviewers
|
||||
labels:
|
||||
- kind/api-change
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +groupName=kubectl.config.k8s.io
|
||||
|
||||
package config // Package config import "k8s.io/kubectl/pkg/config"
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
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 fuzzer
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/randfill"
|
||||
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/kubectl/pkg/config"
|
||||
)
|
||||
|
||||
// Funcs returns the fuzzer functions for the kubectl apis.
|
||||
func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
func(obj *config.Preference, c randfill.Continue) {
|
||||
c.FillNoCustom(obj)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name used in this package
|
||||
const GroupName = "kubectl.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
|
||||
|
||||
var (
|
||||
// SchemeBuilder is the scheme builder with scheme init functions to run for this API package
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
// AddToScheme is a global function that registers this API group & version to a scheme
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// addKnownTypes registers known types to the given scheme
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Preference{},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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 scheme
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/kubectl/pkg/config"
|
||||
"k8s.io/kubectl/pkg/config/v1alpha1"
|
||||
"k8s.io/kubectl/pkg/config/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Scheme defines methods for serializing and deserializing API objects.
|
||||
Scheme = runtime.NewScheme()
|
||||
// StrictCodecs provides methods for retrieving codecs and serializers
|
||||
// for specific versions and content types.
|
||||
StrictCodecs = serializer.NewCodecFactory(Scheme, serializer.EnableStrict)
|
||||
// LenientCodecs provides methods for retrieving codecs and serializers
|
||||
// for specific versions and content types.
|
||||
LenientCodecs = serializer.NewCodecFactory(Scheme, serializer.DisableStrict)
|
||||
)
|
||||
|
||||
func init() {
|
||||
AddToScheme(Scheme)
|
||||
}
|
||||
|
||||
// AddToScheme registers the API group and adds types to a scheme
|
||||
func AddToScheme(scheme *runtime.Scheme) {
|
||||
utilruntime.Must(config.AddToScheme(scheme))
|
||||
utilruntime.Must(v1beta1.AddToScheme(scheme))
|
||||
utilruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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 scheme
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
|
||||
"k8s.io/kubectl/pkg/config/fuzzer"
|
||||
)
|
||||
|
||||
func TestRoundTripTypes(t *testing.T) {
|
||||
roundtrip.RoundTripTestForScheme(t, Scheme, fuzzer.Funcs)
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// Preference stores elements of KubeRC configuration file
|
||||
type Preference struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
// Defaults allow changing default option values of commands.
|
||||
// This is especially useful, when user doesn't want to explicitly
|
||||
// set options each time.
|
||||
// +optional
|
||||
Defaults []CommandDefaults
|
||||
|
||||
// Aliases allow defining command aliases for existing kubectl commands, with optional default option values.
|
||||
// If the alias name collides with a built-in command, built-in command always takes precedence.
|
||||
// Option overrides defined in the defaults section do NOT apply to aliases for the same command.
|
||||
// kubectl [ALIAS NAME] [USER_OPTIONS] [USER_EXPLICIT_ARGS] expands to
|
||||
// kubectl [COMMAND] # built-in command alias points to
|
||||
// [KUBERC_PREPEND_ARGS]
|
||||
// [USER_OPTIONS]
|
||||
// [KUBERC_OPTIONS] # rest of the options that are not passed by user in [USER_OPTIONS]
|
||||
// [USER_EXPLICIT_ARGS]
|
||||
// [KUBERC_APPEND_ARGS]
|
||||
// e.g.
|
||||
// - name: runx
|
||||
// command: run
|
||||
// options:
|
||||
// - name: image
|
||||
// default: nginx
|
||||
// appendArgs:
|
||||
// - --
|
||||
// - custom-arg1
|
||||
// For example, if user invokes "kubectl runx test-pod" command,
|
||||
// this will be expanded to "kubectl run --image=nginx test-pod -- custom-arg1"
|
||||
// - name: getn
|
||||
// command: get
|
||||
// options:
|
||||
// - name: output
|
||||
// default: wide
|
||||
// prependArgs:
|
||||
// - node
|
||||
// "kubectl getn control-plane-1" expands to "kubectl get node control-plane-1 --output=wide"
|
||||
// "kubectl getn control-plane-1 --output=json" expands to "kubectl get node --output=json control-plane-1"
|
||||
// +optional
|
||||
Aliases []AliasOverride
|
||||
}
|
||||
|
||||
// AliasOverride stores the alias definitions.
|
||||
type AliasOverride struct {
|
||||
// Name is the name of alias that can only include alphabetical characters
|
||||
// If the alias name conflicts with the built-in command,
|
||||
// built-in command will be used.
|
||||
Name string
|
||||
// Command is the single or set of commands to execute, such as "set env" or "create"
|
||||
Command string
|
||||
// PrependArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are inserted after the alias name.
|
||||
PrependArgs []string
|
||||
// AppendArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are appended to the USER_ARGS.
|
||||
AppendArgs []string
|
||||
// Options is allocated to store the option definitions of alias.
|
||||
// Options only modify the default value of the option and if
|
||||
// user explicitly passes a value, explicit one is used.
|
||||
Options []CommandOptionDefault
|
||||
}
|
||||
|
||||
// CommandDefaults stores the commands and their associated option's
|
||||
// default values.
|
||||
type CommandDefaults struct {
|
||||
// Command refers to a command whose flag's default value is changed.
|
||||
Command string
|
||||
// Options is a list of options storing different default values.
|
||||
Options []CommandOptionDefault
|
||||
}
|
||||
|
||||
// CommandOptionDefault stores the name and the specified default
|
||||
// value of an option.
|
||||
type CommandOptionDefault struct {
|
||||
// Option name (long form, without dashes).
|
||||
Name string
|
||||
|
||||
// In a string format of a default value. It will be parsed
|
||||
// by kubectl to the compatible value of the option.
|
||||
Default string
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +groupName=kubectl.config.k8s.io
|
||||
// +k8s:conversion-gen=k8s.io/kubectl/pkg/config
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
|
||||
package v1alpha1 // Package v1alpha1 import "k8s.io/kubectl/pkg/config/v1alpha1"
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name used in this package
|
||||
const GroupName = "kubectl.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||
|
||||
var (
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
// addKnownTypes registers known types to the given scheme
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Preference{},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// Preference stores elements of KubeRC configuration file
|
||||
type Preference struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// overrides allows changing default flag values of commands.
|
||||
// This is especially useful, when user doesn't want to explicitly
|
||||
// set flags each time.
|
||||
// +listType=atomic
|
||||
Defaults []CommandDefaults `json:"overrides"`
|
||||
|
||||
// aliases allow defining command aliases for existing kubectl commands, with optional default flag values.
|
||||
// If the alias name collides with a built-in command, built-in command always takes precedence.
|
||||
// Flag overrides defined in the overrides section do NOT apply to aliases for the same command.
|
||||
// kubectl [ALIAS NAME] [USER_FLAGS] [USER_EXPLICIT_ARGS] expands to
|
||||
// kubectl [COMMAND] # built-in command alias points to
|
||||
// [KUBERC_PREPEND_ARGS]
|
||||
// [USER_FLAGS]
|
||||
// [KUBERC_FLAGS] # rest of the flags that are not passed by user in [USER_FLAGS]
|
||||
// [USER_EXPLICIT_ARGS]
|
||||
// [KUBERC_APPEND_ARGS]
|
||||
// e.g.
|
||||
// - name: runx
|
||||
// command: run
|
||||
// flags:
|
||||
// - name: image
|
||||
// default: nginx
|
||||
// appendArgs:
|
||||
// - --
|
||||
// - custom-arg1
|
||||
// For example, if user invokes "kubectl runx test-pod" command,
|
||||
// this will be expanded to "kubectl run --image=nginx test-pod -- custom-arg1"
|
||||
// - name: getn
|
||||
// command: get
|
||||
// flags:
|
||||
// - name: output
|
||||
// default: wide
|
||||
// prependArgs:
|
||||
// - node
|
||||
// "kubectl getn control-plane-1" expands to "kubectl get node control-plane-1 --output=wide"
|
||||
// "kubectl getn control-plane-1 --output=json" expands to "kubectl get node --output=json control-plane-1"
|
||||
// +listType=atomic
|
||||
Aliases []AliasOverride `json:"aliases"`
|
||||
}
|
||||
|
||||
// AliasOverride stores the alias definitions.
|
||||
type AliasOverride struct {
|
||||
// name is the name of alias that can only include alphabetical characters
|
||||
// If the alias name conflicts with the built-in command,
|
||||
// built-in command will be used.
|
||||
Name string `json:"name"`
|
||||
// command is the single or set of commands to execute, such as "set env" or "create"
|
||||
Command string `json:"command"`
|
||||
// prependArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are inserted after the alias name.
|
||||
// +listType=atomic
|
||||
PrependArgs []string `json:"prependArgs,omitempty"`
|
||||
// appendArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are appended to the USER_ARGS.
|
||||
// +listType=atomic
|
||||
AppendArgs []string `json:"appendArgs,omitempty"`
|
||||
// flags is allocated to store the flag definitions of alias.
|
||||
// flags only modifies the default value of the flag and if
|
||||
// user explicitly passes a value, explicit one is used.
|
||||
// +listType=atomic
|
||||
Options []CommandOptionDefault `json:"flags,omitempty"`
|
||||
}
|
||||
|
||||
// CommandDefaults stores the commands and their associated option's
|
||||
// default values.
|
||||
type CommandDefaults struct {
|
||||
// command refers to a command whose flag's default value is changed.
|
||||
Command string `json:"command"`
|
||||
// flags is a list of flags storing different default values.
|
||||
// +listType=atomic
|
||||
Options []CommandOptionDefault `json:"flags"`
|
||||
}
|
||||
|
||||
// CommandOptionDefault stores the name and the specified default
|
||||
// value of an option.
|
||||
type CommandOptionDefault struct {
|
||||
// Flag name (long form, without dashes).
|
||||
Name string `json:"name"`
|
||||
|
||||
// In a string format of a default value. It will be parsed
|
||||
// by kubectl to the compatible value of the flag.
|
||||
Default string `json:"default"`
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
unsafe "unsafe"
|
||||
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
config "k8s.io/kubectl/pkg/config"
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*AliasOverride)(nil), (*config.AliasOverride)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_AliasOverride_To_config_AliasOverride(a.(*AliasOverride), b.(*config.AliasOverride), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.AliasOverride)(nil), (*AliasOverride)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_AliasOverride_To_v1alpha1_AliasOverride(a.(*config.AliasOverride), b.(*AliasOverride), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*CommandDefaults)(nil), (*config.CommandDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_CommandDefaults_To_config_CommandDefaults(a.(*CommandDefaults), b.(*config.CommandDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.CommandDefaults)(nil), (*CommandDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_CommandDefaults_To_v1alpha1_CommandDefaults(a.(*config.CommandDefaults), b.(*CommandDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*CommandOptionDefault)(nil), (*config.CommandOptionDefault)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_CommandOptionDefault_To_config_CommandOptionDefault(a.(*CommandOptionDefault), b.(*config.CommandOptionDefault), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.CommandOptionDefault)(nil), (*CommandOptionDefault)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_CommandOptionDefault_To_v1alpha1_CommandOptionDefault(a.(*config.CommandOptionDefault), b.(*CommandOptionDefault), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*Preference)(nil), (*config.Preference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_Preference_To_config_Preference(a.(*Preference), b.(*config.Preference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.Preference)(nil), (*Preference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_Preference_To_v1alpha1_Preference(a.(*config.Preference), b.(*Preference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_AliasOverride_To_config_AliasOverride(in *AliasOverride, out *config.AliasOverride, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Command = in.Command
|
||||
out.PrependArgs = *(*[]string)(unsafe.Pointer(&in.PrependArgs))
|
||||
out.AppendArgs = *(*[]string)(unsafe.Pointer(&in.AppendArgs))
|
||||
out.Options = *(*[]config.CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_AliasOverride_To_config_AliasOverride is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_AliasOverride_To_config_AliasOverride(in *AliasOverride, out *config.AliasOverride, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_AliasOverride_To_config_AliasOverride(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_AliasOverride_To_v1alpha1_AliasOverride(in *config.AliasOverride, out *AliasOverride, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Command = in.Command
|
||||
out.PrependArgs = *(*[]string)(unsafe.Pointer(&in.PrependArgs))
|
||||
out.AppendArgs = *(*[]string)(unsafe.Pointer(&in.AppendArgs))
|
||||
out.Options = *(*[]CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_AliasOverride_To_v1alpha1_AliasOverride is an autogenerated conversion function.
|
||||
func Convert_config_AliasOverride_To_v1alpha1_AliasOverride(in *config.AliasOverride, out *AliasOverride, s conversion.Scope) error {
|
||||
return autoConvert_config_AliasOverride_To_v1alpha1_AliasOverride(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_CommandDefaults_To_config_CommandDefaults(in *CommandDefaults, out *config.CommandDefaults, s conversion.Scope) error {
|
||||
out.Command = in.Command
|
||||
out.Options = *(*[]config.CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_CommandDefaults_To_config_CommandDefaults is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_CommandDefaults_To_config_CommandDefaults(in *CommandDefaults, out *config.CommandDefaults, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_CommandDefaults_To_config_CommandDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_CommandDefaults_To_v1alpha1_CommandDefaults(in *config.CommandDefaults, out *CommandDefaults, s conversion.Scope) error {
|
||||
out.Command = in.Command
|
||||
out.Options = *(*[]CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_CommandDefaults_To_v1alpha1_CommandDefaults is an autogenerated conversion function.
|
||||
func Convert_config_CommandDefaults_To_v1alpha1_CommandDefaults(in *config.CommandDefaults, out *CommandDefaults, s conversion.Scope) error {
|
||||
return autoConvert_config_CommandDefaults_To_v1alpha1_CommandDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_CommandOptionDefault_To_config_CommandOptionDefault(in *CommandOptionDefault, out *config.CommandOptionDefault, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Default = in.Default
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_CommandOptionDefault_To_config_CommandOptionDefault is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_CommandOptionDefault_To_config_CommandOptionDefault(in *CommandOptionDefault, out *config.CommandOptionDefault, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_CommandOptionDefault_To_config_CommandOptionDefault(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_CommandOptionDefault_To_v1alpha1_CommandOptionDefault(in *config.CommandOptionDefault, out *CommandOptionDefault, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Default = in.Default
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_CommandOptionDefault_To_v1alpha1_CommandOptionDefault is an autogenerated conversion function.
|
||||
func Convert_config_CommandOptionDefault_To_v1alpha1_CommandOptionDefault(in *config.CommandOptionDefault, out *CommandOptionDefault, s conversion.Scope) error {
|
||||
return autoConvert_config_CommandOptionDefault_To_v1alpha1_CommandOptionDefault(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_Preference_To_config_Preference(in *Preference, out *config.Preference, s conversion.Scope) error {
|
||||
out.Defaults = *(*[]config.CommandDefaults)(unsafe.Pointer(&in.Defaults))
|
||||
out.Aliases = *(*[]config.AliasOverride)(unsafe.Pointer(&in.Aliases))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_Preference_To_config_Preference is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_Preference_To_config_Preference(in *Preference, out *config.Preference, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_Preference_To_config_Preference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_Preference_To_v1alpha1_Preference(in *config.Preference, out *Preference, s conversion.Scope) error {
|
||||
out.Defaults = *(*[]CommandDefaults)(unsafe.Pointer(&in.Defaults))
|
||||
out.Aliases = *(*[]AliasOverride)(unsafe.Pointer(&in.Aliases))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_Preference_To_v1alpha1_Preference is an autogenerated conversion function.
|
||||
func Convert_config_Preference_To_v1alpha1_Preference(in *config.Preference, out *Preference, s conversion.Scope) error {
|
||||
return autoConvert_config_Preference_To_v1alpha1_Preference(in, out, s)
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AliasOverride) DeepCopyInto(out *AliasOverride) {
|
||||
*out = *in
|
||||
if in.PrependArgs != nil {
|
||||
in, out := &in.PrependArgs, &out.PrependArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AppendArgs != nil {
|
||||
in, out := &in.AppendArgs, &out.AppendArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AliasOverride.
|
||||
func (in *AliasOverride) DeepCopy() *AliasOverride {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AliasOverride)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandDefaults) DeepCopyInto(out *CommandDefaults) {
|
||||
*out = *in
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandDefaults.
|
||||
func (in *CommandDefaults) DeepCopy() *CommandDefaults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandDefaults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandOptionDefault) DeepCopyInto(out *CommandOptionDefault) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandOptionDefault.
|
||||
func (in *CommandOptionDefault) DeepCopy() *CommandOptionDefault {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandOptionDefault)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preference) DeepCopyInto(out *Preference) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Defaults != nil {
|
||||
in, out := &in.Defaults, &out.Defaults
|
||||
*out = make([]CommandDefaults, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Aliases != nil {
|
||||
in, out := &in.Aliases, &out.Aliases
|
||||
*out = make([]AliasOverride, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Preference.
|
||||
func (in *Preference) DeepCopy() *Preference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Preference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Preference) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +groupName=kubectl.config.k8s.io
|
||||
// +k8s:conversion-gen=k8s.io/kubectl/pkg/config
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
|
||||
package v1beta1 // Package v1beta1 import "k8s.io/kubectl/pkg/config/v1beta1"
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
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 v1beta1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name used in this package
|
||||
const GroupName = "kubectl.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
|
||||
|
||||
var (
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
// addKnownTypes registers known types to the given scheme
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Preference{},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
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 v1beta1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// Preference stores elements of KubeRC configuration file
|
||||
type Preference struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// defaults allow changing default option values of commands.
|
||||
// This is especially useful, when user doesn't want to explicitly
|
||||
// set options each time.
|
||||
// +listType=atomic
|
||||
Defaults []CommandDefaults `json:"defaults"`
|
||||
|
||||
// aliases allow defining command aliases for existing kubectl commands, with optional default option values.
|
||||
// If the alias name collides with a built-in command, built-in command always takes precedence.
|
||||
// Option overrides defined in the defaults section do NOT apply to aliases for the same command.
|
||||
// kubectl [ALIAS NAME] [USER_OPTIONS] [USER_EXPLICIT_ARGS] expands to
|
||||
// kubectl [COMMAND] # built-in command alias points to
|
||||
// [KUBERC_PREPEND_ARGS]
|
||||
// [USER_OPTIONS]
|
||||
// [KUBERC_OPTIONS] # rest of the options that are not passed by user in [USER_OPTIONS]
|
||||
// [USER_EXPLICIT_ARGS]
|
||||
// [KUBERC_APPEND_ARGS]
|
||||
// e.g.
|
||||
// - name: runx
|
||||
// command: run
|
||||
// options:
|
||||
// - name: image
|
||||
// default: nginx
|
||||
// appendArgs:
|
||||
// - --
|
||||
// - custom-arg1
|
||||
// For example, if user invokes "kubectl runx test-pod" command,
|
||||
// this will be expanded to "kubectl run --image=nginx test-pod -- custom-arg1"
|
||||
// - name: getn
|
||||
// command: get
|
||||
// options:
|
||||
// - name: output
|
||||
// default: wide
|
||||
// prependArgs:
|
||||
// - node
|
||||
// "kubectl getn control-plane-1" expands to "kubectl get node control-plane-1 --output=wide"
|
||||
// "kubectl getn control-plane-1 --output=json" expands to "kubectl get node --output=json control-plane-1"
|
||||
// +listType=atomic
|
||||
Aliases []AliasOverride `json:"aliases"`
|
||||
}
|
||||
|
||||
// AliasOverride stores the alias definitions.
|
||||
type AliasOverride struct {
|
||||
// name is the name of alias that can only include alphabetical characters
|
||||
// If the alias name conflicts with the built-in command,
|
||||
// built-in command will be used.
|
||||
Name string `json:"name"`
|
||||
// command is the single or set of commands to execute, such as "set env" or "create"
|
||||
Command string `json:"command"`
|
||||
// prependArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are inserted after the alias name.
|
||||
// +listType=atomic
|
||||
PrependArgs []string `json:"prependArgs,omitempty"`
|
||||
// appendArgs stores the arguments such as resource names, etc.
|
||||
// These arguments are appended to the USER_ARGS.
|
||||
// +listType=atomic
|
||||
AppendArgs []string `json:"appendArgs,omitempty"`
|
||||
// options is allocated to store the option definitions of alias.
|
||||
// options only modify the default value of the option and if
|
||||
// user explicitly passes a value, explicit one is used.
|
||||
// +listType=atomic
|
||||
Options []CommandOptionDefault `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// CommandDefaults stores the commands and their associated option's
|
||||
// default values.
|
||||
type CommandDefaults struct {
|
||||
// command refers to a command whose option's default value is changed.
|
||||
Command string `json:"command"`
|
||||
// options is a list of options storing different default values.
|
||||
// +listType=atomic
|
||||
Options []CommandOptionDefault `json:"options"`
|
||||
}
|
||||
|
||||
// CommandOptionDefault stores the name and the specified default
|
||||
// value of an option.
|
||||
type CommandOptionDefault struct {
|
||||
// Option name (long form, without dashes).
|
||||
Name string `json:"name"`
|
||||
|
||||
// In a string format of a default value. It will be parsed
|
||||
// by kubectl to the compatible value of the option.
|
||||
Default string `json:"default"`
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
unsafe "unsafe"
|
||||
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
config "k8s.io/kubectl/pkg/config"
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*AliasOverride)(nil), (*config.AliasOverride)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_AliasOverride_To_config_AliasOverride(a.(*AliasOverride), b.(*config.AliasOverride), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.AliasOverride)(nil), (*AliasOverride)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_AliasOverride_To_v1beta1_AliasOverride(a.(*config.AliasOverride), b.(*AliasOverride), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*CommandDefaults)(nil), (*config.CommandDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_CommandDefaults_To_config_CommandDefaults(a.(*CommandDefaults), b.(*config.CommandDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.CommandDefaults)(nil), (*CommandDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_CommandDefaults_To_v1beta1_CommandDefaults(a.(*config.CommandDefaults), b.(*CommandDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*CommandOptionDefault)(nil), (*config.CommandOptionDefault)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_CommandOptionDefault_To_config_CommandOptionDefault(a.(*CommandOptionDefault), b.(*config.CommandOptionDefault), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.CommandOptionDefault)(nil), (*CommandOptionDefault)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_CommandOptionDefault_To_v1beta1_CommandOptionDefault(a.(*config.CommandOptionDefault), b.(*CommandOptionDefault), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*Preference)(nil), (*config.Preference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_Preference_To_config_Preference(a.(*Preference), b.(*config.Preference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.Preference)(nil), (*Preference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_Preference_To_v1beta1_Preference(a.(*config.Preference), b.(*Preference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_AliasOverride_To_config_AliasOverride(in *AliasOverride, out *config.AliasOverride, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Command = in.Command
|
||||
out.PrependArgs = *(*[]string)(unsafe.Pointer(&in.PrependArgs))
|
||||
out.AppendArgs = *(*[]string)(unsafe.Pointer(&in.AppendArgs))
|
||||
out.Options = *(*[]config.CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_AliasOverride_To_config_AliasOverride is an autogenerated conversion function.
|
||||
func Convert_v1beta1_AliasOverride_To_config_AliasOverride(in *AliasOverride, out *config.AliasOverride, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_AliasOverride_To_config_AliasOverride(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_AliasOverride_To_v1beta1_AliasOverride(in *config.AliasOverride, out *AliasOverride, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Command = in.Command
|
||||
out.PrependArgs = *(*[]string)(unsafe.Pointer(&in.PrependArgs))
|
||||
out.AppendArgs = *(*[]string)(unsafe.Pointer(&in.AppendArgs))
|
||||
out.Options = *(*[]CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_AliasOverride_To_v1beta1_AliasOverride is an autogenerated conversion function.
|
||||
func Convert_config_AliasOverride_To_v1beta1_AliasOverride(in *config.AliasOverride, out *AliasOverride, s conversion.Scope) error {
|
||||
return autoConvert_config_AliasOverride_To_v1beta1_AliasOverride(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_CommandDefaults_To_config_CommandDefaults(in *CommandDefaults, out *config.CommandDefaults, s conversion.Scope) error {
|
||||
out.Command = in.Command
|
||||
out.Options = *(*[]config.CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_CommandDefaults_To_config_CommandDefaults is an autogenerated conversion function.
|
||||
func Convert_v1beta1_CommandDefaults_To_config_CommandDefaults(in *CommandDefaults, out *config.CommandDefaults, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_CommandDefaults_To_config_CommandDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_CommandDefaults_To_v1beta1_CommandDefaults(in *config.CommandDefaults, out *CommandDefaults, s conversion.Scope) error {
|
||||
out.Command = in.Command
|
||||
out.Options = *(*[]CommandOptionDefault)(unsafe.Pointer(&in.Options))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_CommandDefaults_To_v1beta1_CommandDefaults is an autogenerated conversion function.
|
||||
func Convert_config_CommandDefaults_To_v1beta1_CommandDefaults(in *config.CommandDefaults, out *CommandDefaults, s conversion.Scope) error {
|
||||
return autoConvert_config_CommandDefaults_To_v1beta1_CommandDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_CommandOptionDefault_To_config_CommandOptionDefault(in *CommandOptionDefault, out *config.CommandOptionDefault, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Default = in.Default
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_CommandOptionDefault_To_config_CommandOptionDefault is an autogenerated conversion function.
|
||||
func Convert_v1beta1_CommandOptionDefault_To_config_CommandOptionDefault(in *CommandOptionDefault, out *config.CommandOptionDefault, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_CommandOptionDefault_To_config_CommandOptionDefault(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_CommandOptionDefault_To_v1beta1_CommandOptionDefault(in *config.CommandOptionDefault, out *CommandOptionDefault, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Default = in.Default
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_CommandOptionDefault_To_v1beta1_CommandOptionDefault is an autogenerated conversion function.
|
||||
func Convert_config_CommandOptionDefault_To_v1beta1_CommandOptionDefault(in *config.CommandOptionDefault, out *CommandOptionDefault, s conversion.Scope) error {
|
||||
return autoConvert_config_CommandOptionDefault_To_v1beta1_CommandOptionDefault(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_Preference_To_config_Preference(in *Preference, out *config.Preference, s conversion.Scope) error {
|
||||
out.Defaults = *(*[]config.CommandDefaults)(unsafe.Pointer(&in.Defaults))
|
||||
out.Aliases = *(*[]config.AliasOverride)(unsafe.Pointer(&in.Aliases))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_Preference_To_config_Preference is an autogenerated conversion function.
|
||||
func Convert_v1beta1_Preference_To_config_Preference(in *Preference, out *config.Preference, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_Preference_To_config_Preference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_Preference_To_v1beta1_Preference(in *config.Preference, out *Preference, s conversion.Scope) error {
|
||||
out.Defaults = *(*[]CommandDefaults)(unsafe.Pointer(&in.Defaults))
|
||||
out.Aliases = *(*[]AliasOverride)(unsafe.Pointer(&in.Aliases))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_Preference_To_v1beta1_Preference is an autogenerated conversion function.
|
||||
func Convert_config_Preference_To_v1beta1_Preference(in *config.Preference, out *Preference, s conversion.Scope) error {
|
||||
return autoConvert_config_Preference_To_v1beta1_Preference(in, out, s)
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AliasOverride) DeepCopyInto(out *AliasOverride) {
|
||||
*out = *in
|
||||
if in.PrependArgs != nil {
|
||||
in, out := &in.PrependArgs, &out.PrependArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AppendArgs != nil {
|
||||
in, out := &in.AppendArgs, &out.AppendArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AliasOverride.
|
||||
func (in *AliasOverride) DeepCopy() *AliasOverride {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AliasOverride)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandDefaults) DeepCopyInto(out *CommandDefaults) {
|
||||
*out = *in
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandDefaults.
|
||||
func (in *CommandDefaults) DeepCopy() *CommandDefaults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandDefaults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandOptionDefault) DeepCopyInto(out *CommandOptionDefault) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandOptionDefault.
|
||||
func (in *CommandOptionDefault) DeepCopy() *CommandOptionDefault {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandOptionDefault)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preference) DeepCopyInto(out *Preference) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Defaults != nil {
|
||||
in, out := &in.Defaults, &out.Defaults
|
||||
*out = make([]CommandDefaults, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Aliases != nil {
|
||||
in, out := &in.Aliases, &out.Aliases
|
||||
*out = make([]AliasOverride, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Preference.
|
||||
func (in *Preference) DeepCopy() *Preference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Preference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Preference) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AliasOverride) DeepCopyInto(out *AliasOverride) {
|
||||
*out = *in
|
||||
if in.PrependArgs != nil {
|
||||
in, out := &in.PrependArgs, &out.PrependArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AppendArgs != nil {
|
||||
in, out := &in.AppendArgs, &out.AppendArgs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AliasOverride.
|
||||
func (in *AliasOverride) DeepCopy() *AliasOverride {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AliasOverride)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandDefaults) DeepCopyInto(out *CommandDefaults) {
|
||||
*out = *in
|
||||
if in.Options != nil {
|
||||
in, out := &in.Options, &out.Options
|
||||
*out = make([]CommandOptionDefault, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandDefaults.
|
||||
func (in *CommandDefaults) DeepCopy() *CommandDefaults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandDefaults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CommandOptionDefault) DeepCopyInto(out *CommandOptionDefault) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommandOptionDefault.
|
||||
func (in *CommandOptionDefault) DeepCopy() *CommandOptionDefault {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CommandOptionDefault)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Preference) DeepCopyInto(out *Preference) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.Defaults != nil {
|
||||
in, out := &in.Defaults, &out.Defaults
|
||||
*out = make([]CommandDefaults, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Aliases != nil {
|
||||
in, out := &in.Aliases, &out.Aliases
|
||||
*out = make([]AliasOverride, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Preference.
|
||||
func (in *Preference) DeepCopy() *Preference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Preference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Preference) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -22,9 +22,11 @@ import (
|
|||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -97,7 +99,7 @@ const (
|
|||
|
||||
var (
|
||||
// globally skipped annotations
|
||||
skipAnnotations = sets.NewString(corev1.LastAppliedConfigAnnotation)
|
||||
skipAnnotations = sets.New[string](corev1.LastAppliedConfigAnnotation)
|
||||
|
||||
// DescriberFn gives a way to easily override the function for unit testing if needed
|
||||
DescriberFn DescriberFunc = Describer
|
||||
|
@ -217,6 +219,8 @@ func describerMap(clientConfig *rest.Config) (map[schema.GroupKind]ResourceDescr
|
|||
{Group: networkingv1.GroupName, Kind: "IngressClass"}: &IngressClassDescriber{c},
|
||||
{Group: networkingv1beta1.GroupName, Kind: "ServiceCIDR"}: &ServiceCIDRDescriber{c},
|
||||
{Group: networkingv1beta1.GroupName, Kind: "IPAddress"}: &IPAddressDescriber{c},
|
||||
{Group: networkingv1.GroupName, Kind: "ServiceCIDR"}: &ServiceCIDRDescriber{c},
|
||||
{Group: networkingv1.GroupName, Kind: "IPAddress"}: &IPAddressDescriber{c},
|
||||
{Group: batchv1.GroupName, Kind: "Job"}: &JobDescriber{c},
|
||||
{Group: batchv1.GroupName, Kind: "CronJob"}: &CronJobDescriber{c},
|
||||
{Group: batchv1beta1.GroupName, Kind: "CronJob"}: &CronJobDescriber{c},
|
||||
|
@ -317,7 +321,7 @@ func printUnstructuredContent(w PrefixWriter, level int, content map[string]inte
|
|||
switch typedValue := value.(type) {
|
||||
case map[string]interface{}:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
if slice.Contains[string](skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, "%s:\n", smartLabelFor(field))
|
||||
|
@ -325,7 +329,7 @@ func printUnstructuredContent(w PrefixWriter, level int, content map[string]inte
|
|||
|
||||
case []interface{}:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
if slice.Contains[string](skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, "%s:\n", smartLabelFor(field))
|
||||
|
@ -340,7 +344,7 @@ func printUnstructuredContent(w PrefixWriter, level int, content map[string]inte
|
|||
|
||||
default:
|
||||
skipExpr := fmt.Sprintf("%s.%s", skipPrefix, field)
|
||||
if slice.ContainsString(skip, skipExpr, nil) {
|
||||
if slice.Contains[string](skip, skipExpr, nil) {
|
||||
continue
|
||||
}
|
||||
w.Write(level, "%s:\t%v\n", smartLabelFor(field), typedValue)
|
||||
|
@ -365,7 +369,7 @@ func smartLabelFor(field string) string {
|
|||
continue
|
||||
}
|
||||
|
||||
if slice.ContainsString(commonAcronyms, strings.ToUpper(part), nil) {
|
||||
if slice.Contains[string](commonAcronyms, strings.ToUpper(part), nil) {
|
||||
part = strings.ToUpper(part)
|
||||
} else {
|
||||
part = strings.Title(part)
|
||||
|
@ -1082,15 +1086,17 @@ func printProjectedVolumeSource(projected *corev1.ProjectedVolumeSource, w Prefi
|
|||
w.Write(LEVEL_2, "Type:\tProjected (a volume that contains injected data from multiple sources)\n")
|
||||
for _, source := range projected.Sources {
|
||||
if source.Secret != nil {
|
||||
optional := source.Secret.Optional != nil && *source.Secret.Optional
|
||||
w.Write(LEVEL_2, "SecretName:\t%v\n"+
|
||||
" SecretOptionalName:\t%v\n",
|
||||
source.Secret.Name, source.Secret.Optional)
|
||||
" Optional:\t%v\n",
|
||||
source.Secret.Name, optional)
|
||||
} else if source.DownwardAPI != nil {
|
||||
w.Write(LEVEL_2, "DownwardAPI:\ttrue\n")
|
||||
} else if source.ConfigMap != nil {
|
||||
optional := source.ConfigMap.Optional != nil && *source.ConfigMap.Optional
|
||||
w.Write(LEVEL_2, "ConfigMapName:\t%v\n"+
|
||||
" ConfigMapOptional:\t%v\n",
|
||||
source.ConfigMap.Name, source.ConfigMap.Optional)
|
||||
" Optional:\t%v\n",
|
||||
source.ConfigMap.Name, optional)
|
||||
} else if source.ServiceAccountToken != nil {
|
||||
w.Write(LEVEL_2, "TokenExpirationSeconds:\t%d\n",
|
||||
*source.ServiceAccountToken.ExpirationSeconds)
|
||||
|
@ -1449,10 +1455,10 @@ func printCSIPersistentVolumeSource(csi *corev1.CSIPersistentVolumeSource, w Pre
|
|||
}
|
||||
|
||||
func printCSIPersistentVolumeAttributesMultiline(w PrefixWriter, title string, annotations map[string]string) {
|
||||
printCSIPersistentVolumeAttributesMultilineIndent(w, "", title, "\t", annotations, sets.NewString())
|
||||
printCSIPersistentVolumeAttributesMultilineIndent(w, "", title, "\t", annotations, sets.New[string]())
|
||||
}
|
||||
|
||||
func printCSIPersistentVolumeAttributesMultilineIndent(w PrefixWriter, initialIndent, title, innerIndent string, attributes map[string]string, skip sets.String) {
|
||||
func printCSIPersistentVolumeAttributesMultilineIndent(w PrefixWriter, initialIndent, title, innerIndent string, attributes map[string]string, skip sets.Set[string]) {
|
||||
w.Write(LEVEL_2, "%s%s:%s", initialIndent, title, innerIndent)
|
||||
|
||||
if len(attributes) == 0 {
|
||||
|
@ -2128,8 +2134,8 @@ func describeVolumeClaimTemplates(templates []corev1.PersistentVolumeClaim, w Pr
|
|||
for _, pvc := range templates {
|
||||
w.Write(LEVEL_1, "Name:\t%s\n", pvc.Name)
|
||||
w.Write(LEVEL_1, "StorageClass:\t%s\n", storageutil.GetPersistentVolumeClaimClass(&pvc))
|
||||
printLabelsMultilineWithIndent(w, " ", "Labels", "\t", pvc.Labels, sets.NewString())
|
||||
printLabelsMultilineWithIndent(w, " ", "Annotations", "\t", pvc.Annotations, sets.NewString())
|
||||
printLabelsMultilineWithIndent(w, " ", "Labels", "\t", pvc.Labels, sets.New[string]())
|
||||
printLabelsMultilineWithIndent(w, " ", "Annotations", "\t", pvc.Annotations, sets.New[string]())
|
||||
if capacity, ok := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; ok {
|
||||
w.Write(LEVEL_1, "Capacity:\t%s\n", capacity.String())
|
||||
} else {
|
||||
|
@ -2523,18 +2529,14 @@ func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings
|
|||
events, _ = searchEvents(d.CoreV1(), daemon, describerSettings.ChunkSize)
|
||||
}
|
||||
|
||||
return describeDaemonSet(daemon, events, running, waiting, succeeded, failed)
|
||||
return describeDaemonSet(daemon, selector, events, running, waiting, succeeded, failed)
|
||||
}
|
||||
|
||||
func describeDaemonSet(daemon *appsv1.DaemonSet, events *corev1.EventList, running, waiting, succeeded, failed int) (string, error) {
|
||||
func describeDaemonSet(daemon *appsv1.DaemonSet, selector labels.Selector, events *corev1.EventList, running, waiting, succeeded, failed int) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%s\n", daemon.Name)
|
||||
selector, err := metav1.LabelSelectorAsSelector(daemon.Spec.Selector)
|
||||
if err != nil {
|
||||
// this shouldn't happen if LabelSelector passed validation
|
||||
return err
|
||||
}
|
||||
w.Write(LEVEL_0, "Namespace:\t%s\n", daemon.Namespace)
|
||||
w.Write(LEVEL_0, "Selector:\t%s\n", selector)
|
||||
w.Write(LEVEL_0, "Node-Selector:\t%s\n", labels.FormatLabels(daemon.Spec.Template.Spec.NodeSelector))
|
||||
printLabelsMultiline(w, "Labels", daemon.Labels)
|
||||
|
@ -2580,12 +2582,12 @@ func describeSecret(secret *corev1.Secret) (string, error) {
|
|||
w.Write(LEVEL_0, "\nType:\t%s\n", secret.Type)
|
||||
|
||||
w.Write(LEVEL_0, "\nData\n====\n")
|
||||
for k, v := range secret.Data {
|
||||
for _, k := range slices.Sorted(maps.Keys(secret.Data)) {
|
||||
switch {
|
||||
case k == corev1.ServiceAccountTokenKey && secret.Type == corev1.SecretTypeServiceAccountToken:
|
||||
w.Write(LEVEL_0, "%s:\t%s\n", k, string(v))
|
||||
w.Write(LEVEL_0, "%s:\t%s\n", k, string(secret.Data[k]))
|
||||
default:
|
||||
w.Write(LEVEL_0, "%s:\t%d bytes\n", k, len(v))
|
||||
w.Write(LEVEL_0, "%s:\t%d bytes\n", k, len(secret.Data[k]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2887,6 +2889,14 @@ type ServiceCIDRDescriber struct {
|
|||
func (c *ServiceCIDRDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
|
||||
var events *corev1.EventList
|
||||
|
||||
svcV1, err := c.client.NetworkingV1().ServiceCIDRs().Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if describerSettings.ShowEvents {
|
||||
events, _ = searchEvents(c.client.CoreV1(), svcV1, describerSettings.ChunkSize)
|
||||
}
|
||||
return c.describeServiceCIDRV1(svcV1, events)
|
||||
}
|
||||
|
||||
svcV1beta1, err := c.client.NetworkingV1beta1().ServiceCIDRs().Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if describerSettings.ShowEvents {
|
||||
|
@ -2897,6 +2907,37 @@ func (c *ServiceCIDRDescriber) Describe(namespace, name string, describerSetting
|
|||
return "", err
|
||||
}
|
||||
|
||||
func (c *ServiceCIDRDescriber) describeServiceCIDRV1(svc *networkingv1.ServiceCIDR, events *corev1.EventList) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%v\n", svc.Name)
|
||||
printLabelsMultiline(w, "Labels", svc.Labels)
|
||||
printAnnotationsMultiline(w, "Annotations", svc.Annotations)
|
||||
|
||||
w.Write(LEVEL_0, "CIDRs:\t%v\n", strings.Join(svc.Spec.CIDRs, ", "))
|
||||
|
||||
if len(svc.Status.Conditions) > 0 {
|
||||
w.Write(LEVEL_0, "Status:\n")
|
||||
w.Write(LEVEL_0, "Conditions:\n")
|
||||
w.Write(LEVEL_1, "Type\tStatus\tLastTransitionTime\tReason\tMessage\n")
|
||||
w.Write(LEVEL_1, "----\t------\t------------------\t------\t-------\n")
|
||||
for _, c := range svc.Status.Conditions {
|
||||
w.Write(LEVEL_1, "%v\t%v\t%s\t%v\t%v\n",
|
||||
c.Type,
|
||||
c.Status,
|
||||
c.LastTransitionTime.Time.Format(time.RFC1123Z),
|
||||
c.Reason,
|
||||
c.Message)
|
||||
}
|
||||
}
|
||||
|
||||
if events != nil {
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ServiceCIDRDescriber) describeServiceCIDRV1beta1(svc *networkingv1beta1.ServiceCIDR, events *corev1.EventList) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
|
@ -2936,6 +2977,14 @@ type IPAddressDescriber struct {
|
|||
func (c *IPAddressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
|
||||
var events *corev1.EventList
|
||||
|
||||
ipV1, err := c.client.NetworkingV1().IPAddresses().Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if describerSettings.ShowEvents {
|
||||
events, _ = searchEvents(c.client.CoreV1(), ipV1, describerSettings.ChunkSize)
|
||||
}
|
||||
return c.describeIPAddressV1(ipV1, events)
|
||||
}
|
||||
|
||||
ipV1beta1, err := c.client.NetworkingV1beta1().IPAddresses().Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if describerSettings.ShowEvents {
|
||||
|
@ -2946,6 +2995,28 @@ func (c *IPAddressDescriber) Describe(namespace, name string, describerSettings
|
|||
return "", err
|
||||
}
|
||||
|
||||
func (c *IPAddressDescriber) describeIPAddressV1(ip *networkingv1.IPAddress, events *corev1.EventList) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%v\n", ip.Name)
|
||||
printLabelsMultiline(w, "Labels", ip.Labels)
|
||||
printAnnotationsMultiline(w, "Annotations", ip.Annotations)
|
||||
|
||||
if ip.Spec.ParentRef != nil {
|
||||
w.Write(LEVEL_0, "Parent Reference:\n")
|
||||
w.Write(LEVEL_1, "Group:\t%v\n", ip.Spec.ParentRef.Group)
|
||||
w.Write(LEVEL_1, "Resource:\t%v\n", ip.Spec.ParentRef.Resource)
|
||||
w.Write(LEVEL_1, "Namespace:\t%v\n", ip.Spec.ParentRef.Namespace)
|
||||
w.Write(LEVEL_1, "Name:\t%v\n", ip.Spec.ParentRef.Name)
|
||||
}
|
||||
|
||||
if events != nil {
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *IPAddressDescriber) describeIPAddressV1beta1(ip *networkingv1beta1.IPAddress, events *corev1.EventList) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
|
@ -3086,6 +3157,9 @@ func describeService(service *corev1.Service, endpointSlices []discoveryv1.Endpo
|
|||
if len(service.Spec.LoadBalancerSourceRanges) > 0 {
|
||||
w.Write(LEVEL_0, "LoadBalancer Source Ranges:\t%v\n", strings.Join(service.Spec.LoadBalancerSourceRanges, ","))
|
||||
}
|
||||
if service.Spec.TrafficDistribution != nil {
|
||||
w.Write(LEVEL_0, "Traffic Distribution:\t%s\n", *service.Spec.TrafficDistribution)
|
||||
}
|
||||
if events != nil {
|
||||
DescribeEvents(events, w)
|
||||
}
|
||||
|
@ -3340,7 +3414,7 @@ func describeEndpointSliceV1beta1(eps *discoveryv1beta1.EndpointSlice, events *c
|
|||
w.Write(LEVEL_2, "TargetRef:\t%s/%s\n", endpoint.TargetRef.Kind, endpoint.TargetRef.Name)
|
||||
}
|
||||
|
||||
printLabelsMultilineWithIndent(w, " ", "Topology", "\t", endpoint.Topology, sets.NewString())
|
||||
printLabelsMultilineWithIndent(w, " ", "Topology", "\t", endpoint.Topology, sets.New[string]())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3368,7 +3442,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
|
|||
|
||||
// missingSecrets is the set of all secrets present in the
|
||||
// serviceAccount but not present in the set of existing secrets.
|
||||
missingSecrets := sets.NewString()
|
||||
missingSecrets := sets.New[string]()
|
||||
secrets := corev1.SecretList{}
|
||||
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
|
||||
func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
|
@ -3385,7 +3459,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
|
|||
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.NewString()
|
||||
existingSecrets := sets.New[string]()
|
||||
|
||||
for _, s := range secrets.Items {
|
||||
if s.Type == corev1.SecretTypeServiceAccountToken {
|
||||
|
@ -3418,7 +3492,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
|
|||
return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
|
||||
}
|
||||
|
||||
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.String, events *corev1.EventList) (string, error) {
|
||||
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.Set[string], events *corev1.EventList) (string, error) {
|
||||
return tabbedString(func(out io.Writer) error {
|
||||
w := NewPrefixWriter(out)
|
||||
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
|
||||
|
@ -3452,7 +3526,7 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []core
|
|||
mountHeader: mountSecretNames,
|
||||
tokenHeader: tokenSecretNames,
|
||||
}
|
||||
for _, header := range sets.StringKeySet(types).List() {
|
||||
for _, header := range sets.List(sets.KeySet(types)) {
|
||||
names := types[header]
|
||||
if len(names) == 0 {
|
||||
w.Write(LEVEL_0, "%s\t<none>\n", header)
|
||||
|
@ -4494,13 +4568,15 @@ func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings
|
|||
printAnnotationsMultiline(w, "Annotations", configMap.Annotations)
|
||||
|
||||
w.Write(LEVEL_0, "\nData\n====\n")
|
||||
for k, v := range configMap.Data {
|
||||
for _, k := range slices.Sorted(maps.Keys(configMap.Data)) {
|
||||
v := configMap.Data[k]
|
||||
w.Write(LEVEL_0, "%s:\n----\n", k)
|
||||
w.Write(LEVEL_0, "%s\n", string(v))
|
||||
w.Write(LEVEL_0, "\n")
|
||||
}
|
||||
w.Write(LEVEL_0, "\nBinaryData\n====\n")
|
||||
for k, v := range configMap.BinaryData {
|
||||
for _, k := range slices.Sorted(maps.Keys(configMap.BinaryData)) {
|
||||
v := configMap.BinaryData[k]
|
||||
w.Write(LEVEL_0, "%s: %s bytes\n", k, strconv.Itoa(len(v)))
|
||||
}
|
||||
w.Write(LEVEL_0, "\n")
|
||||
|
@ -5150,11 +5226,11 @@ func (fn typeFunc) Describe(exact interface{}, extra ...interface{}) (string, er
|
|||
|
||||
// printLabelsMultiline prints multiple labels with a proper alignment.
|
||||
func printLabelsMultiline(w PrefixWriter, title string, labels map[string]string) {
|
||||
printLabelsMultilineWithIndent(w, "", title, "\t", labels, sets.NewString())
|
||||
printLabelsMultilineWithIndent(w, "", title, "\t", labels, sets.New[string]())
|
||||
}
|
||||
|
||||
// printLabelsMultiline prints multiple labels with a user-defined alignment.
|
||||
func printLabelsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, labels map[string]string, skip sets.String) {
|
||||
func printLabelsMultilineWithIndent(w PrefixWriter, initialIndent, title, innerIndent string, labels map[string]string, skip sets.Set[string]) {
|
||||
w.Write(LEVEL_0, "%s%s:%s", initialIndent, title, innerIndent)
|
||||
|
||||
if len(labels) == 0 {
|
||||
|
@ -5568,7 +5644,7 @@ func backendStringer(backend *networkingv1beta1.IngressBackend) string {
|
|||
// * a node-role.kubernetes.io/<role>="" label
|
||||
// * a kubernetes.io/role="<role>" label
|
||||
func findNodeRoles(node *corev1.Node) []string {
|
||||
roles := sets.NewString()
|
||||
roles := sets.New[string]()
|
||||
for k, v := range node.Labels {
|
||||
switch {
|
||||
case strings.HasPrefix(k, LabelNodeRolePrefix):
|
||||
|
@ -5580,14 +5656,14 @@ func findNodeRoles(node *corev1.Node) []string {
|
|||
roles.Insert(v)
|
||||
}
|
||||
}
|
||||
return roles.List()
|
||||
return sets.List(roles)
|
||||
}
|
||||
|
||||
// ingressLoadBalancerStatusStringerV1 behaves mostly like a string interface and converts the given status to a string.
|
||||
// `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
|
||||
func ingressLoadBalancerStatusStringerV1(s networkingv1.IngressLoadBalancerStatus, wide bool) string {
|
||||
ingress := s.Ingress
|
||||
result := sets.NewString()
|
||||
result := sets.New[string]()
|
||||
for i := range ingress {
|
||||
if ingress[i].IP != "" {
|
||||
result.Insert(ingress[i].IP)
|
||||
|
@ -5596,7 +5672,7 @@ func ingressLoadBalancerStatusStringerV1(s networkingv1.IngressLoadBalancerStatu
|
|||
}
|
||||
}
|
||||
|
||||
r := strings.Join(result.List(), ",")
|
||||
r := strings.Join(sets.List(result), ",")
|
||||
if !wide && len(r) > LoadBalancerWidth {
|
||||
r = r[0:(LoadBalancerWidth-3)] + "..."
|
||||
}
|
||||
|
@ -5607,7 +5683,7 @@ func ingressLoadBalancerStatusStringerV1(s networkingv1.IngressLoadBalancerStatu
|
|||
// `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
|
||||
func ingressLoadBalancerStatusStringerV1beta1(s networkingv1beta1.IngressLoadBalancerStatus, wide bool) string {
|
||||
ingress := s.Ingress
|
||||
result := sets.NewString()
|
||||
result := sets.New[string]()
|
||||
for i := range ingress {
|
||||
if ingress[i].IP != "" {
|
||||
result.Insert(ingress[i].IP)
|
||||
|
@ -5616,7 +5692,7 @@ func ingressLoadBalancerStatusStringerV1beta1(s networkingv1beta1.IngressLoadBal
|
|||
}
|
||||
}
|
||||
|
||||
r := strings.Join(result.List(), ",")
|
||||
r := strings.Join(sets.List(result), ",")
|
||||
if !wide && len(r) > LoadBalancerWidth {
|
||||
r = r[0:(LoadBalancerWidth-3)] + "..."
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package describe
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"maps"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -350,27 +351,79 @@ func TestDescribeTopologySpreadConstraints(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDescribeSecret(t *testing.T) {
|
||||
fake := fake.NewSimpleClientset(&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
testCases := []struct {
|
||||
description string
|
||||
data map[string][]byte // secret key -> secret in bytes
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
description: "alphabetical ordering",
|
||||
data: map[string][]byte{
|
||||
"username": []byte("YWRtaW4="),
|
||||
"password": []byte("MWYyZDFlMmU2N2Rm"),
|
||||
},
|
||||
expected: []string{"password", "username"},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"username": []byte("YWRtaW4="),
|
||||
"password": []byte("MWYyZDFlMmU2N2Rm"),
|
||||
{
|
||||
description: "uppercase takes precedence",
|
||||
data: map[string][]byte{
|
||||
"text": []byte("a3ViZXJuZXRlcwo="),
|
||||
"Text": []byte("dGhpcyBpcyBhIHRlc3QK"),
|
||||
"tExt": []byte("d2VpcmQgY2FzaW5nCg=="),
|
||||
},
|
||||
expected: []string{"Text", "tExt", "text"},
|
||||
},
|
||||
{
|
||||
description: "numbers take precedence",
|
||||
data: map[string][]byte{
|
||||
"key_1": []byte("c29tZV9zZWNyZXQK"),
|
||||
"1_key": []byte("c29tZV90ZXh0Cg=="),
|
||||
},
|
||||
expected: []string{"1_key", "key_1"},
|
||||
},
|
||||
})
|
||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||
d := SecretDescriber{c}
|
||||
out, err := d.Describe("foo", "bar", DescriberSettings{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "bar") || !strings.Contains(out, "foo") || !strings.Contains(out, "username") || !strings.Contains(out, "8 bytes") || !strings.Contains(out, "password") || !strings.Contains(out, "16 bytes") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
if strings.Contains(out, "YWRtaW4=") || strings.Contains(out, "MWYyZDFlMmU2N2Rm") {
|
||||
t.Errorf("sensitive data should not be shown, unexpected out: %s", out)
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.description, func(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Data: testCase.data,
|
||||
}
|
||||
fake := fake.NewSimpleClientset(secret)
|
||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||
d := SecretDescriber{c}
|
||||
|
||||
out, err := d.Describe("foo", "bar", DescriberSettings{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
for value := range maps.Values(testCase.data) {
|
||||
if strings.Contains(out, string(value)) {
|
||||
t.Errorf("sensitive data should not be shown, unexpected out: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
expectedOut := `Name: bar
|
||||
Namespace: foo
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
|
||||
Type:
|
||||
|
||||
Data
|
||||
====`
|
||||
|
||||
for _, expectedKey := range testCase.expected {
|
||||
expectedOut = fmt.Sprintf("%s\n%s: %d bytes", expectedOut, expectedKey, len(testCase.data[expectedKey]))
|
||||
}
|
||||
expectedOut = fmt.Sprintf("%s\n", expectedOut)
|
||||
|
||||
assert.Equal(t, expectedOut, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -660,6 +713,32 @@ func TestDescribeConfigMap(t *testing.T) {
|
|||
if !strings.Contains(out, "binarykey1") || !strings.Contains(out, "5 bytes") || !strings.Contains(out, "binarykey2") || !strings.Contains(out, "6 bytes") {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
}
|
||||
|
||||
expectedOut := `Name: mycm
|
||||
Namespace: foo
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
|
||||
Data
|
||||
====
|
||||
key1:
|
||||
----
|
||||
value1
|
||||
|
||||
key2:
|
||||
----
|
||||
value2
|
||||
|
||||
|
||||
BinaryData
|
||||
====
|
||||
binarykey1: 5 bytes
|
||||
binarykey2: 6 bytes
|
||||
|
||||
Events: <none>
|
||||
`
|
||||
|
||||
assert.Equal(t, expectedOut, out)
|
||||
}
|
||||
|
||||
func TestDescribeLimitRange(t *testing.T) {
|
||||
|
@ -728,6 +807,7 @@ func getResourceList(cpu, memory string) corev1.ResourceList {
|
|||
|
||||
func TestDescribeService(t *testing.T) {
|
||||
singleStack := corev1.IPFamilyPolicySingleStack
|
||||
preferClose := corev1.ServiceTrafficDistributionPreferClose
|
||||
testCases := []struct {
|
||||
name string
|
||||
service *corev1.Service
|
||||
|
@ -1086,6 +1166,54 @@ func TestDescribeService(t *testing.T) {
|
|||
Events: <none>
|
||||
`)[1:],
|
||||
},
|
||||
{
|
||||
name: "test-TrafficDistribution",
|
||||
service: &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeLoadBalancer,
|
||||
Ports: []corev1.ServicePort{{
|
||||
Name: "port-tcp",
|
||||
Port: 8080,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
TargetPort: intstr.FromString("targetPort"),
|
||||
NodePort: 31111,
|
||||
}},
|
||||
Selector: map[string]string{"blah": "heh"},
|
||||
ClusterIP: "1.2.3.4",
|
||||
IPFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
|
||||
LoadBalancerIP: "5.6.7.8",
|
||||
SessionAffinity: corev1.ServiceAffinityNone,
|
||||
ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyLocal,
|
||||
TrafficDistribution: &preferClose,
|
||||
HealthCheckNodePort: 32222,
|
||||
},
|
||||
},
|
||||
expected: dedent.Dedent(`
|
||||
Name: bar
|
||||
Namespace: foo
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Selector: blah=heh
|
||||
Type: LoadBalancer
|
||||
IP Families: IPv4
|
||||
IP: 1.2.3.4
|
||||
IPs: <none>
|
||||
Desired LoadBalancer IP: 5.6.7.8
|
||||
Port: port-tcp 8080/TCP
|
||||
TargetPort: targetPort/TCP
|
||||
NodePort: port-tcp 31111/TCP
|
||||
Endpoints: <none>
|
||||
Session Affinity: None
|
||||
External Traffic Policy: Local
|
||||
HealthCheck NodePort: 32222
|
||||
Traffic Distribution: PreferClose
|
||||
Events: <none>
|
||||
`)[1:],
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
@ -6397,6 +6525,41 @@ func TestDescribeStatefulSet(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDescribeDaemonSet(t *testing.T) {
|
||||
fake := fake.NewSimpleClientset(&appsv1.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Spec: appsv1.DaemonSetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"node-role.kubernetes.io/control-plane": "true"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{Image: "mytest-image:latest"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
d := DaemonSetDescriber{fake}
|
||||
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
expectedOutputs := []string{
|
||||
"bar", "foo", "Containers:", "mytest-image:latest", "Selector", "node-role.kubernetes.io/control-plane=true",
|
||||
}
|
||||
for _, o := range expectedOutputs {
|
||||
if !strings.Contains(out, o) {
|
||||
t.Errorf("unexpected out: %s", out)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeEndpointSlice(t *testing.T) {
|
||||
protocolTCP := corev1.ProtocolTCP
|
||||
port80 := int32(80)
|
||||
|
@ -6590,6 +6753,54 @@ Events: <none>` + "\n",
|
|||
Labels: <none>
|
||||
Annotations: <none>
|
||||
CIDRs: fd00:1:1::/64
|
||||
Events: <none>` + "\n",
|
||||
},
|
||||
"ServiceCIDR v1": {
|
||||
input: fake.NewSimpleClientset(&networkingv1.ServiceCIDR{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.123",
|
||||
},
|
||||
Spec: networkingv1.ServiceCIDRSpec{
|
||||
CIDRs: []string{"10.1.0.0/16", "fd00:1:1::/64"},
|
||||
},
|
||||
}),
|
||||
|
||||
output: `Name: foo.123
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
CIDRs: 10.1.0.0/16, fd00:1:1::/64
|
||||
Events: <none>` + "\n",
|
||||
},
|
||||
"ServiceCIDR v1 IPv4": {
|
||||
input: fake.NewSimpleClientset(&networkingv1.ServiceCIDR{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.123",
|
||||
},
|
||||
Spec: networkingv1.ServiceCIDRSpec{
|
||||
CIDRs: []string{"10.1.0.0/16"},
|
||||
},
|
||||
}),
|
||||
|
||||
output: `Name: foo.123
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
CIDRs: 10.1.0.0/16
|
||||
Events: <none>` + "\n",
|
||||
},
|
||||
"ServiceCIDR v1 IPv6": {
|
||||
input: fake.NewSimpleClientset(&networkingv1.ServiceCIDR{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.123",
|
||||
},
|
||||
Spec: networkingv1.ServiceCIDRSpec{
|
||||
CIDRs: []string{"fd00:1:1::/64"},
|
||||
},
|
||||
}),
|
||||
|
||||
output: `Name: foo.123
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
CIDRs: fd00:1:1::/64
|
||||
Events: <none>` + "\n",
|
||||
},
|
||||
}
|
||||
|
@ -6633,6 +6844,31 @@ func TestDescribeIPAddress(t *testing.T) {
|
|||
output: `Name: foo.123
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Parent Reference:
|
||||
Group: mygroup
|
||||
Resource: myresource
|
||||
Namespace: mynamespace
|
||||
Name: myname
|
||||
Events: <none>` + "\n",
|
||||
},
|
||||
"IPAddress v1": {
|
||||
input: fake.NewSimpleClientset(&networkingv1.IPAddress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo.123",
|
||||
},
|
||||
Spec: networkingv1.IPAddressSpec{
|
||||
ParentRef: &networkingv1.ParentReference{
|
||||
Group: "mygroup",
|
||||
Resource: "myresource",
|
||||
Namespace: "mynamespace",
|
||||
Name: "myname",
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
output: `Name: foo.123
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Parent Reference:
|
||||
Group: mygroup
|
||||
Resource: myresource
|
||||
|
@ -6906,3 +7142,43 @@ func TestDescribeSeccompProfile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeProjectedVolumesOptionalSecret(t *testing.T) {
|
||||
fake := fake.NewSimpleClientset(&corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
Namespace: "foo",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "optional-secret",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
Projected: &corev1.ProjectedVolumeSource{
|
||||
Sources: []corev1.VolumeProjection{
|
||||
{
|
||||
Secret: &corev1.SecretProjection{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: "optional-secret",
|
||||
},
|
||||
Optional: ptr.To(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
|
||||
d := PodDescriber{c}
|
||||
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
expectedOut := "SecretName: optional-secret\n Optional: true"
|
||||
if !strings.Contains(out, expectedOut) {
|
||||
t.Errorf("expected to find %q in output: %q", expectedOut, out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package drain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type newCordonHelperFromRuntimeObjectTestCase struct {
|
||||
name string
|
||||
nodeObject runtime.Object
|
||||
expectError bool
|
||||
expected *CordonHelper
|
||||
}
|
||||
|
||||
func TestNewCordonHelperFromRuntimeObject(t *testing.T) {
|
||||
tests := []newCordonHelperFromRuntimeObjectTestCase{
|
||||
{
|
||||
name: "valid node object",
|
||||
nodeObject: &corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-node",
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
expected: &CordonHelper{
|
||||
node: &corev1.Node{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Node",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-node",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid object type",
|
||||
nodeObject: &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pod",
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
_ = corev1.AddToScheme(scheme)
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
Kind: "Node",
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
helper, err := NewCordonHelperFromRuntimeObject(tt.nodeObject, scheme, gvk)
|
||||
if tt.expectError && err == nil {
|
||||
t.Error("Expected error but got none")
|
||||
}
|
||||
if !tt.expectError && err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if !tt.expectError && helper == nil {
|
||||
t.Error("Expected non-nil helper")
|
||||
}
|
||||
if tt.expected != nil && helper != nil {
|
||||
if diff := cmp.Diff(tt.expected.node, helper.node); diff != "" {
|
||||
t.Errorf("Node mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type updateIfRequiredTestCase struct {
|
||||
name string
|
||||
currentState bool
|
||||
desiredState bool
|
||||
expectUpdated bool
|
||||
}
|
||||
|
||||
func TestUpdateIfRequired(t *testing.T) {
|
||||
tests := []updateIfRequiredTestCase{
|
||||
{
|
||||
name: "no change required",
|
||||
currentState: true,
|
||||
desiredState: true,
|
||||
expectUpdated: false,
|
||||
},
|
||||
{
|
||||
name: "update required - cordon",
|
||||
currentState: false,
|
||||
desiredState: true,
|
||||
expectUpdated: true,
|
||||
},
|
||||
{
|
||||
name: "update required - uncordon",
|
||||
currentState: true,
|
||||
desiredState: false,
|
||||
expectUpdated: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
node := &corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-node",
|
||||
},
|
||||
Spec: corev1.NodeSpec{
|
||||
Unschedulable: tt.currentState,
|
||||
},
|
||||
}
|
||||
|
||||
helper := NewCordonHelper(node)
|
||||
updated := helper.UpdateIfRequired(tt.desiredState)
|
||||
if updated != tt.expectUpdated {
|
||||
t.Errorf("Expected UpdateIfRequired to return %v, got %v", tt.expectUpdated, updated)
|
||||
}
|
||||
if helper.desired != tt.desiredState {
|
||||
t.Errorf("Expected desired state to be %v, got %v", tt.desiredState, helper.desired)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -6399,7 +6399,7 @@
|
|||
"x-kubernetes-patch-strategy": "merge"
|
||||
},
|
||||
"initContainers": {
|
||||
"description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
|
||||
"description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
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 kuberc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
"k8s.io/kubectl/pkg/config"
|
||||
)
|
||||
|
||||
const RecommendedKubeRCFileName = "kuberc"
|
||||
|
||||
var (
|
||||
RecommendedConfigDir = filepath.Join(homedir.HomeDir(), clientcmd.RecommendedHomeDir)
|
||||
RecommendedKubeRCFile = filepath.Join(RecommendedConfigDir, RecommendedKubeRCFileName)
|
||||
|
||||
aliasNameRegex = regexp.MustCompile("^[a-zA-Z]+$")
|
||||
shortHandRegex = regexp.MustCompile("^-[a-zA-Z]+$")
|
||||
)
|
||||
|
||||
// PreferencesHandler is responsible for setting default flags
|
||||
// arguments based on user's kuberc configuration.
|
||||
type PreferencesHandler interface {
|
||||
AddFlags(flags *pflag.FlagSet)
|
||||
Apply(rootCmd *cobra.Command, args []string, errOut io.Writer) ([]string, error)
|
||||
}
|
||||
|
||||
// Preferences stores the kuberc file coming either from environment variable
|
||||
// or file from set in flag or the default kuberc path.
|
||||
type Preferences struct {
|
||||
getPreferencesFunc func(kuberc string, errOut io.Writer) (*config.Preference, error)
|
||||
|
||||
aliases map[string]struct{}
|
||||
}
|
||||
|
||||
// NewPreferences returns initialized Prefrences object.
|
||||
func NewPreferences() PreferencesHandler {
|
||||
return &Preferences{
|
||||
getPreferencesFunc: DefaultGetPreferences,
|
||||
aliases: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
type aliasing struct {
|
||||
appendArgs []string
|
||||
prependArgs []string
|
||||
flags []config.CommandOptionDefault
|
||||
command *cobra.Command
|
||||
}
|
||||
|
||||
// AddFlags adds kuberc related flags into the command.
|
||||
func (p *Preferences) AddFlags(flags *pflag.FlagSet) {
|
||||
flags.String("kuberc", "", "Path to the kuberc file to use for preferences. This can be disabled by exporting KUBECTL_KUBERC=false feature gate or turning off the feature KUBERC=off.")
|
||||
}
|
||||
|
||||
// Apply firstly applies the aliases in the preferences file and secondly overrides
|
||||
// the default values of flags.
|
||||
func (p *Preferences) Apply(rootCmd *cobra.Command, args []string, errOut io.Writer) ([]string, error) {
|
||||
if len(args) <= 1 {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
kubercPath, err := getExplicitKuberc(args)
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
kuberc, err := p.getPreferencesFunc(kubercPath, errOut)
|
||||
if err != nil {
|
||||
return args, fmt.Errorf("kuberc error %w", err)
|
||||
}
|
||||
|
||||
if kuberc == nil {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
err = validate(kuberc)
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
|
||||
args, err = p.applyAliases(rootCmd, kuberc, args, errOut)
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
err = p.applyOverrides(rootCmd, kuberc, args, errOut)
|
||||
if err != nil {
|
||||
return args, err
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// applyOverrides finds the command and sets the defaulted flag values in kuberc.
|
||||
func (p *Preferences) applyOverrides(rootCmd *cobra.Command, kuberc *config.Preference, args []string, errOut io.Writer) error {
|
||||
args = args[1:]
|
||||
cmd, _, err := rootCmd.Find(args)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, c := range kuberc.Defaults {
|
||||
parsedCmds := strings.Fields(c.Command)
|
||||
overrideCmd, _, err := rootCmd.Find(parsedCmds)
|
||||
if err != nil {
|
||||
fmt.Fprintf(errOut, "Warning: command %q not found to set kuberc override\n", c.Command)
|
||||
continue
|
||||
}
|
||||
if overrideCmd.Name() != cmd.Name() {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := p.aliases[cmd.Name()]; ok {
|
||||
return fmt.Errorf("alias %s can not be overridden", cmd.Name())
|
||||
}
|
||||
|
||||
// This function triggers merging the persistent flags in the parent commands.
|
||||
_ = cmd.InheritedFlags()
|
||||
|
||||
allShorthands := make(map[string]struct{})
|
||||
cmd.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||
if flag.Shorthand != "" {
|
||||
allShorthands[flag.Shorthand] = struct{}{}
|
||||
}
|
||||
})
|
||||
|
||||
for _, fl := range c.Options {
|
||||
existingFlag := cmd.Flag(fl.Name)
|
||||
if existingFlag == nil {
|
||||
return fmt.Errorf("invalid flag %s for command %s", fl.Name, c.Command)
|
||||
}
|
||||
if searchInArgs(existingFlag.Name, existingFlag.Shorthand, allShorthands, args) {
|
||||
// Don't modify the value implicitly, if it is passed in args explicitly
|
||||
continue
|
||||
}
|
||||
err = cmd.Flags().Set(fl.Name, fl.Default)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not apply override value %s to flag %s in command %s err: %w", fl.Default, fl.Name, c.Command, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyAliases firstly appends all defined aliases in kuberc file to the root command.
|
||||
// Since there may be several alias definitions belonging to the same command, it extracts the
|
||||
// alias that is currently executed from args. After that it sets the flag definitions in alias as default values
|
||||
// of the command. Lastly, others parameters (e.g. resources, etc.) that are passed as arguments in kuberc
|
||||
// is appended into the command args.
|
||||
func (p *Preferences) applyAliases(rootCmd *cobra.Command, kuberc *config.Preference, args []string, errOut io.Writer) ([]string, error) {
|
||||
_, _, err := rootCmd.Find(args[1:])
|
||||
if err == nil {
|
||||
// Command is found, no need to continue for aliasing
|
||||
return args, nil
|
||||
}
|
||||
|
||||
var aliasArgs *aliasing
|
||||
|
||||
var commandName string // first "non-flag" arguments
|
||||
var commandIndex int
|
||||
for index, arg := range args[1:] {
|
||||
if !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, cobra.ShellCompRequestCmd) {
|
||||
commandName = arg
|
||||
commandIndex = index + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, alias := range kuberc.Aliases {
|
||||
p.aliases[alias.Name] = struct{}{}
|
||||
if alias.Name != commandName {
|
||||
continue
|
||||
}
|
||||
|
||||
// do not allow shadowing built-ins
|
||||
if _, _, err := rootCmd.Find([]string{alias.Name}); err == nil {
|
||||
fmt.Fprintf(errOut, "Warning: Setting alias %q to a built-in command is not supported\n", alias.Name)
|
||||
break
|
||||
}
|
||||
|
||||
commands := strings.Fields(alias.Command)
|
||||
existingCmd, flags, err := rootCmd.Find(commands)
|
||||
if err != nil {
|
||||
return args, fmt.Errorf("command %q not found to set alias %q: %v", alias.Command, alias.Name, flags)
|
||||
}
|
||||
|
||||
newCmd := *existingCmd
|
||||
newCmd.Use = alias.Name
|
||||
newCmd.Aliases = []string{}
|
||||
aliasCmd := &newCmd
|
||||
|
||||
aliasArgs = &aliasing{
|
||||
prependArgs: alias.PrependArgs,
|
||||
appendArgs: alias.AppendArgs,
|
||||
flags: alias.Options,
|
||||
command: aliasCmd,
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if aliasArgs == nil {
|
||||
// pursue with the current behavior.
|
||||
// This might be a built-in command, external plugin, etc.
|
||||
return args, nil
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(aliasArgs.command)
|
||||
|
||||
foundAliasCmd, _, err := rootCmd.Find([]string{commandName})
|
||||
if err != nil {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// This function triggers merging the persistent flags in the parent commands.
|
||||
_ = foundAliasCmd.InheritedFlags()
|
||||
|
||||
allShorthands := make(map[string]struct{})
|
||||
foundAliasCmd.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||
if flag.Shorthand != "" {
|
||||
allShorthands[flag.Shorthand] = struct{}{}
|
||||
}
|
||||
})
|
||||
|
||||
for _, fl := range aliasArgs.flags {
|
||||
existingFlag := foundAliasCmd.Flag(fl.Name)
|
||||
if existingFlag == nil {
|
||||
return args, fmt.Errorf("invalid alias flag %s in alias %s", fl.Name, args[0])
|
||||
}
|
||||
if searchInArgs(existingFlag.Name, existingFlag.Shorthand, allShorthands, args) {
|
||||
// Don't modify the value implicitly, if it is passed in args explicitly
|
||||
continue
|
||||
}
|
||||
err = foundAliasCmd.Flags().Set(fl.Name, fl.Default)
|
||||
if err != nil {
|
||||
return args, fmt.Errorf("could not apply value %s to flag %s in alias %s err: %w", fl.Default, fl.Name, args[0], err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(aliasArgs.prependArgs) > 0 {
|
||||
// prependArgs defined in kuberc should be inserted after the alias name.
|
||||
if commandIndex+1 >= len(args) {
|
||||
// command is the last item, we simply append just like appendArgs
|
||||
args = append(args, aliasArgs.prependArgs...)
|
||||
} else {
|
||||
args = append(args[:commandIndex+1], append(aliasArgs.prependArgs, args[commandIndex+1:]...)...)
|
||||
}
|
||||
}
|
||||
if len(aliasArgs.appendArgs) > 0 {
|
||||
// appendArgs defined in kuberc should be appended to actual args.
|
||||
args = append(args, aliasArgs.appendArgs...)
|
||||
}
|
||||
// Cobra (command.go#L1078) appends only root command's args into the actual args and ignores the others.
|
||||
// We are appending the additional args defined in kuberc in here and
|
||||
// expect that it will be passed along to the actual command.
|
||||
rootCmd.SetArgs(args[1:])
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// DefaultGetPreferences returns KubeRCConfiguration.
|
||||
// If users sets kuberc file explicitly in --kuberc flag, it has the highest
|
||||
// priority. If not specified, it looks for in KUBERC environment variable.
|
||||
// If KUBERC is also not set, it falls back to default .kuberc file at the same location
|
||||
// where kubeconfig's defaults are residing in.
|
||||
// If KUBERC is set to "off", kuberc will be turned off and original behaviors in kubectl will be applied.
|
||||
func DefaultGetPreferences(kuberc string, errOut io.Writer) (*config.Preference, error) {
|
||||
if val := os.Getenv("KUBERC"); val == "off" {
|
||||
if kuberc != "" {
|
||||
return nil, fmt.Errorf("disabling kuberc via KUBERC=off and passing kuberc flag are mutually exclusive")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
kubeRCFile := RecommendedKubeRCFile
|
||||
explicitly := false
|
||||
if kuberc != "" {
|
||||
kubeRCFile = kuberc
|
||||
explicitly = true
|
||||
}
|
||||
|
||||
if kubeRCFile == "" && os.Getenv("KUBERC") != "" {
|
||||
kubeRCFile = os.Getenv("KUBERC")
|
||||
explicitly = true
|
||||
}
|
||||
|
||||
preference, err := decodePreference(kubeRCFile)
|
||||
switch {
|
||||
case preference != nil && runtime.IsStrictDecodingError(err):
|
||||
// just warn about strict decoding errors if we got a usable Preference object back
|
||||
fmt.Fprintf(errOut, "kuberc: ignoring strict decoding error in %s: %v", kubeRCFile, err) //nolint:errcheck
|
||||
return preference, nil
|
||||
|
||||
case explicitly && err != nil:
|
||||
// if explicitly requested, error on any error other than a StrictDecodingError
|
||||
return nil, fmt.Errorf("kuberc: %w", err)
|
||||
|
||||
case !explicitly && os.IsNotExist(err):
|
||||
// if not explicitly requested, silently ignore missing kuberc
|
||||
return nil, nil
|
||||
|
||||
case !explicitly && err != nil:
|
||||
// if not explicitly requested, only warn on any other error
|
||||
fmt.Fprintf(errOut, "kuberc: no preferences loaded from %s: %v", kubeRCFile, err) //nolint:errcheck
|
||||
return nil, nil
|
||||
|
||||
default:
|
||||
return preference, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Normally, we should extract this value directly from kuberc flag.
|
||||
// However, flag values are set during the command execution and
|
||||
// we are in very early stages to prepare commands prior to execute them.
|
||||
// Besides, we only need kuberc flag value in this stage.
|
||||
func getExplicitKuberc(args []string) (string, error) {
|
||||
var kubercPath string
|
||||
for i, arg := range args {
|
||||
if arg == "--" {
|
||||
// flags after "--" does not represent any flag of
|
||||
// the command. We should short cut the iteration in here.
|
||||
break
|
||||
}
|
||||
if arg == "--kuberc" {
|
||||
if i+1 < len(args) {
|
||||
kubercPath = args[i+1]
|
||||
break
|
||||
}
|
||||
return "", fmt.Errorf("kuberc file is not found")
|
||||
} else if strings.Contains(arg, "--kuberc=") {
|
||||
parg := strings.Split(arg, "=")
|
||||
if len(parg) > 1 && parg[1] != "" {
|
||||
kubercPath = parg[1]
|
||||
break
|
||||
}
|
||||
return "", fmt.Errorf("kuberc file is not found")
|
||||
}
|
||||
}
|
||||
|
||||
if kubercPath == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return kubercPath, nil
|
||||
}
|
||||
|
||||
// searchInArgs searches the given key in the args and returns
|
||||
// true, if it finds. Otherwise, it returns false.
|
||||
func searchInArgs(flagName string, shorthand string, allShorthands map[string]struct{}, args []string) bool {
|
||||
for _, arg := range args {
|
||||
// if flag is set in args in "--flag value" or "--flag=value" format,
|
||||
// we should return it as found
|
||||
if fmt.Sprintf("--%s", flagName) == arg || strings.HasPrefix(arg, fmt.Sprintf("--%s=", flagName)) {
|
||||
return true
|
||||
}
|
||||
if shorthand == "" {
|
||||
continue
|
||||
}
|
||||
// shorthand can be in "-n value" or "-nvalue" format
|
||||
// it is guaranteed that shorthand is one letter. So that
|
||||
// checking just the prefix -oyaml also finds --output.
|
||||
if strings.HasPrefix(arg, fmt.Sprintf("-%s", shorthand)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !shortHandRegex.MatchString(arg) {
|
||||
continue
|
||||
}
|
||||
|
||||
// remove prefix "-"
|
||||
arg = arg[1:]
|
||||
// short hands can be in a combined "-abc" format.
|
||||
// First we need to ensure that all the values are shorthand to safely search ours.
|
||||
// Because we know that "-abcvalue" is not valid. So that we need to be sure that if we find
|
||||
// "b" it correctly refers to the shorthand "b" not arbitrary value "-cargb".
|
||||
arbitraryFound := false
|
||||
for _, runeValue := range shorthand {
|
||||
if _, ok := allShorthands[string(runeValue)]; !ok {
|
||||
arbitraryFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if arbitraryFound {
|
||||
continue
|
||||
}
|
||||
// verified that all values are short hand. Now search ours
|
||||
if strings.Contains(arg, shorthand) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validate(plugin *config.Preference) error {
|
||||
validateFlag := func(flags []config.CommandOptionDefault) error {
|
||||
for _, flag := range flags {
|
||||
if strings.HasPrefix(flag.Name, "-") {
|
||||
return fmt.Errorf("flag name %s should be in long form without dashes", flag.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
aliases := make(map[string]struct{})
|
||||
for _, alias := range plugin.Aliases {
|
||||
if !aliasNameRegex.MatchString(alias.Name) {
|
||||
return fmt.Errorf("invalid alias name, can only include alphabetical characters")
|
||||
}
|
||||
|
||||
if err := validateFlag(alias.Options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := aliases[alias.Name]; ok {
|
||||
return fmt.Errorf("duplicate alias name %s", alias.Name)
|
||||
}
|
||||
aliases[alias.Name] = struct{}{}
|
||||
}
|
||||
|
||||
for _, override := range plugin.Defaults {
|
||||
if err := validateFlag(override.Options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue