Compare commits
192 Commits
v0.32.0-al
...
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 | |
|
d0bc9691f3 | |
|
133d258d8e | |
|
1811ebafa9 | |
|
7ff7f80add | |
|
6e4fe32a45 | |
|
9a565d149e | |
|
1f9df3421a | |
|
2bb31e1a0f | |
|
2cccb2df6a | |
|
4ced6667a4 | |
|
b7988e1775 | |
|
2ebf801b89 | |
|
b22c40d47c | |
|
4b9836ac7a | |
|
913dcdf388 | |
|
e91a2f1400 | |
|
2fceb953a8 | |
|
dc24746c3a | |
|
6989e5df8d | |
|
739aca7b89 | |
|
7ec5989b2d | |
|
c5abf9ddc7 | |
|
a886863a93 | |
|
4532c53760 | |
|
3cd67e0222 | |
|
69f03b081e | |
|
b67296a41b | |
|
3553001de4 | |
|
c7e911a6b5 | |
|
bb485d40d9 | |
|
dc6975b78c | |
|
af49e676b2 | |
|
a499023358 | |
|
1014b1d5f2 | |
|
816c382177 | |
|
4c8c153513 | |
|
484ede079f | |
|
73f8c2980c | |
|
87fbd1d830 | |
|
e4a11c9548 | |
|
4657409ea3 | |
|
9a6ad3e08e |
|
@ -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
|
||||
|
|
83
go.mod
83
go.mod
|
@ -2,70 +2,67 @@
|
|||
|
||||
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
|
||||
github.com/chai2010/gettext-go v1.0.2
|
||||
github.com/daviddengcn/go-colortext v1.0.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
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.19.0
|
||||
github.com/onsi/gomega v1.33.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/onsi/ginkgo/v2 v2.21.0
|
||||
github.com/onsi/gomega v1.35.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-20241011135352-a432ebea6002
|
||||
k8s.io/apimachinery v0.0.0-20241015204703-8a237eeb80a5
|
||||
k8s.io/cli-runtime v0.0.0-20241016063424-fff087a7ee41
|
||||
k8s.io/client-go v0.0.0-20241016060006-235936510e4e
|
||||
k8s.io/component-base v0.0.0-20241016061006-25e024afbb68
|
||||
k8s.io/component-helpers v0.0.0-20241016061132-1148b937ba05
|
||||
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-20240827152857-f7e401e7b4c2
|
||||
k8s.io/metrics v0.0.0-20241016063243-345e1e6d8c23
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
|
||||
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.1
|
||||
sigs.k8s.io/kustomize/kustomize/v5 v5.6.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0
|
||||
sigs.k8s.io/randfill v1.0.0
|
||||
sigs.k8s.io/structured-merge-diff/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/pprof v0.0.0-20240727154555-813a5fbdbec8 // 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
|
||||
|
@ -73,25 +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.34.2 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // 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
|
||||
)
|
||||
|
|
164
go.sum
164
go.sum
|
@ -8,16 +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/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE=
|
||||
github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=
|
||||
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=
|
||||
|
@ -26,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=
|
||||
|
@ -44,37 +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/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
|
||||
github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A=
|
||||
github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE=
|
||||
github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrSEUQ=
|
||||
github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA=
|
||||
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-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
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=
|
||||
|
@ -103,37 +91,37 @@ 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=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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=
|
||||
|
@ -145,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=
|
||||
|
@ -164,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=
|
||||
|
@ -197,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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
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=
|
||||
|
@ -206,41 +194,41 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP
|
|||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
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-20241011135352-a432ebea6002 h1:RvGxsDUWpmBJSDbSf4VxbU+ITNerDQ/xYIF7xjK9NsA=
|
||||
k8s.io/api v0.0.0-20241011135352-a432ebea6002/go.mod h1:KfoQfYB8K80kj2ZJhrj/CwEwZSAkQYsRFP+xICkEFLk=
|
||||
k8s.io/apimachinery v0.0.0-20241015204703-8a237eeb80a5 h1:NXz4EXwteIYZtkUuppBmXFp5WAkICE0zrnCWLUuk0rw=
|
||||
k8s.io/apimachinery v0.0.0-20241015204703-8a237eeb80a5/go.mod h1:y/FzDt/GaPgPceo5rJcCtD4qW5l8SwtbzESSMGEY6P8=
|
||||
k8s.io/cli-runtime v0.0.0-20241016063424-fff087a7ee41 h1:T9edUbtjRQ7koWJ0pUFvaxEAWTYY5PSLNg2h79f+tdQ=
|
||||
k8s.io/cli-runtime v0.0.0-20241016063424-fff087a7ee41/go.mod h1:GHLA5Ak52NKXIDKHmcJLdYf3+PM8eK1Qoey6xQjcpSg=
|
||||
k8s.io/client-go v0.0.0-20241016060006-235936510e4e h1:zCqT70WMPNR7KVO2FiKiUiqNhBoKvM6RVaSiW0NLm/0=
|
||||
k8s.io/client-go v0.0.0-20241016060006-235936510e4e/go.mod h1:e0M6GvCmf6gBgbMYYlfiXolMjNUdFhRdW3zcN9JjLEE=
|
||||
k8s.io/component-base v0.0.0-20241016061006-25e024afbb68 h1:nMWCgixumsmMMaxXXeXaYdsbnDv+47ABQ0Twdd0W8ag=
|
||||
k8s.io/component-base v0.0.0-20241016061006-25e024afbb68/go.mod h1:2E89xEMHU6917vgXuDf+U3hsAywyfjKFi+WWicoF4js=
|
||||
k8s.io/component-helpers v0.0.0-20241016061132-1148b937ba05 h1:ccxwwJFjnrQR+6nB35i9N2w3FDKqPGnfg/qR3sMptoo=
|
||||
k8s.io/component-helpers v0.0.0-20241016061132-1148b937ba05/go.mod h1:IvCWnpqGNsK1qpo6OhE2bfUAkSBGw+wZMf8RX5mVMA4=
|
||||
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-20240827152857-f7e401e7b4c2 h1:GKE9U8BH16uynoxQii0auTjmmmuZ3O0LFMN6S0lPPhI=
|
||||
k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA=
|
||||
k8s.io/metrics v0.0.0-20241016063243-345e1e6d8c23 h1:dI/KtJd4wjs+K2zZP0/M5n3mlrB7DuWEUiHxiE1mEus=
|
||||
k8s.io/metrics v0.0.0-20241016063243-345e1e6d8c23/go.mod h1:Bg9KwCkiLYVUIVXzbVI2CYibCa/Q5SuZG5xDmxkDo5w=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/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.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
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{
|
||||
|
|
|
@ -50,7 +50,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
"k8s.io/kubectl/pkg/util/prune"
|
||||
"k8s.io/kubectl/pkg/util/slice"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
"k8s.io/kubectl/pkg/validation"
|
||||
)
|
||||
|
@ -181,8 +180,6 @@ var (
|
|||
|
||||
var ApplySetToolVersion = version.Get().GitVersion
|
||||
|
||||
var supportedSubresources = []string{"status", "scale"}
|
||||
|
||||
// NewApplyFlags returns a default ApplyFlags
|
||||
func NewApplyFlags(streams genericiooptions.IOStreams) *ApplyFlags {
|
||||
return &ApplyFlags{
|
||||
|
@ -240,7 +237,7 @@ func (flags *ApplyFlags) AddFlags(cmd *cobra.Command) {
|
|||
cmdutil.AddPruningFlags(cmd, &flags.Prune, &flags.PruneAllowlist, &flags.All, &flags.ApplySetRef)
|
||||
cmd.Flags().BoolVar(&flags.Overwrite, "overwrite", flags.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
|
||||
cmd.Flags().BoolVar(&flags.OpenAPIPatch, "openapi-patch", flags.OpenAPIPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
|
||||
cmdutil.AddSubresourceFlags(cmd, &flags.Subresource, "If specified, apply will operate on the subresource of the requested object. Only allowed when using --server-side.", supportedSubresources...)
|
||||
cmdutil.AddSubresourceFlags(cmd, &flags.Subresource, "If specified, apply will operate on the subresource of the requested object. Only allowed when using --server-side.")
|
||||
}
|
||||
|
||||
// ToOptions converts from CLI inputs to runtime inputs
|
||||
|
@ -445,9 +442,6 @@ func (o *ApplyOptions) Validate() error {
|
|||
}
|
||||
}
|
||||
}
|
||||
if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) {
|
||||
return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources)
|
||||
}
|
||||
if len(o.Subresource) > 0 && !o.ServerSideApply {
|
||||
return fmt.Errorf("--subresource can only be specified for --server-side")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -17,13 +17,12 @@ limitations under the License.
|
|||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
|
@ -60,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 {
|
||||
|
@ -119,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
|
||||
|
@ -177,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,7 +386,7 @@ func (p *Patcher) deleteAndCreate(original runtime.Object, modified []byte, name
|
|||
return modified, nil, err
|
||||
}
|
||||
// TODO: use wait
|
||||
if err := wait.PollImmediate(1*time.Second, p.Timeout, func() (bool, error) {
|
||||
if err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, p.Timeout, true, func(ctx context.Context) (bool, error) {
|
||||
if _, err := p.Helper.Get(namespace, name); !apierrors.IsNotFound(err) {
|
||||
return false, 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
|
||||
|
|
|
@ -161,7 +161,7 @@ func TestRunAccessCheck(t *testing.T) {
|
|||
test.serverErr
|
||||
}),
|
||||
}
|
||||
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}
|
||||
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}, ContentType: runtime.ContentTypeJSON}}
|
||||
|
||||
if err := test.o.Complete(tf, test.args); err != nil {
|
||||
t.Errorf("%s: %v", test.name, err)
|
||||
|
@ -196,6 +196,7 @@ func TestRunAccessList(t *testing.T) {
|
|||
" [/version] [] [get]\n"
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
tf.ClientConfigVal.ContentType = runtime.ContentTypeJSON
|
||||
defer tf.Cleanup()
|
||||
|
||||
ns := scheme.Codecs.WithoutConversion()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
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 autoscale
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
type validateTestCase struct {
|
||||
name string
|
||||
options *AutoscaleOptions
|
||||
expectedError error
|
||||
}
|
||||
|
||||
func TestAutoscaleValidate(t *testing.T) {
|
||||
tests := []validateTestCase{
|
||||
{
|
||||
name: "valid options",
|
||||
options: &AutoscaleOptions{
|
||||
Max: 10,
|
||||
Min: 1,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "max less than 1",
|
||||
options: &AutoscaleOptions{
|
||||
Max: 0,
|
||||
Min: 1,
|
||||
},
|
||||
expectedError: fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: 0"),
|
||||
},
|
||||
{
|
||||
name: "min greater than max",
|
||||
options: &AutoscaleOptions{
|
||||
Max: 1,
|
||||
Min: 2,
|
||||
},
|
||||
expectedError: fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: 1, min: 2"),
|
||||
},
|
||||
{
|
||||
name: "zero min replicas",
|
||||
options: &AutoscaleOptions{
|
||||
Max: 5,
|
||||
Min: 0,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "negative min replicas",
|
||||
options: &AutoscaleOptions{
|
||||
Max: 5,
|
||||
Min: -2,
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errorGot := tc.options.Validate()
|
||||
assert.Equal(t, tc.expectedError, errorGot)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type createHorizontalPodAutoscalerTestCase struct {
|
||||
name string
|
||||
options *AutoscaleOptions
|
||||
refName string
|
||||
mapping *meta.RESTMapping
|
||||
expectedHPAV2 *autoscalingv2.HorizontalPodAutoscaler
|
||||
expectedHPAV1 *autoscalingv1.HorizontalPodAutoscaler
|
||||
}
|
||||
|
||||
func TestCreateHorizontalPodAutoscalerV2(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",
|
||||
},
|
||||
},
|
||||
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",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-1",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(10),
|
||||
TargetCPUUtilizationPercentage: 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",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-2",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-2",
|
||||
},
|
||||
MinReplicas: nil,
|
||||
MaxReplicas: int32(10),
|
||||
TargetCPUUtilizationPercentage: 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",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-3",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "deployment-3",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(-1),
|
||||
TargetCPUUtilizationPercentage: 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",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "custom-name-4",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.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",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicaset-hpa",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Name: "frontend",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(1)),
|
||||
MaxReplicas: int32(5),
|
||||
TargetCPUUtilizationPercentage: 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",
|
||||
},
|
||||
},
|
||||
expectedHPAV1: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "statefulset-hpa",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "StatefulSet",
|
||||
Name: "web",
|
||||
},
|
||||
MinReplicas: ptr.To(int32(2)),
|
||||
MaxReplicas: int32(8),
|
||||
TargetCPUUtilizationPercentage: ptr.To(int32(60)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
hpa := tc.options.createHorizontalPodAutoscalerV1(tc.refName, tc.mapping)
|
||||
assert.Equal(t, tc.expectedHPAV1, hpa)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
ct "github.com/daviddengcn/go-colortext"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -156,12 +155,8 @@ func (o *ClusterInfoOptions) Run() error {
|
|||
}
|
||||
|
||||
func printService(out io.Writer, name, link string) {
|
||||
ct.ChangeColor(ct.Green, false, ct.None, false)
|
||||
fmt.Fprint(out, name)
|
||||
ct.ResetColor()
|
||||
fmt.Fprint(out, " is running at ")
|
||||
ct.ChangeColor(ct.Yellow, false, ct.None, false)
|
||||
fmt.Fprint(out, link)
|
||||
ct.ResetColor()
|
||||
fmt.Fprintln(out, "")
|
||||
}
|
||||
|
|
|
@ -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(`
|
||||
|
@ -212,9 +215,7 @@ func (o *DebugOptions) AddFlags(cmd *cobra.Command) {
|
|||
cmd.Flags().StringVar(&o.TargetContainer, "target", "", i18n.T("When using an ephemeral container, target processes in this container name."))
|
||||
cmd.Flags().BoolVarP(&o.TTY, "tty", "t", o.TTY, i18n.T("Allocate a TTY for the debugging container."))
|
||||
cmd.Flags().StringVar(&o.Profile, "profile", ProfileLegacy, i18n.T(`Options are "legacy", "general", "baseline", "netadmin", "restricted" or "sysadmin".`))
|
||||
if !cmdutil.DebugCustomProfile.IsDisabled() {
|
||||
cmd.Flags().StringVar(&o.CustomProfileFile, "custom", o.CustomProfileFile, i18n.T("Path to a JSON or YAML file containing a partial container spec to customize built-in debug profiles."))
|
||||
}
|
||||
cmd.Flags().StringVar(&o.CustomProfileFile, "custom", o.CustomProfileFile, i18n.T("Path to a JSON or YAML file containing a partial container spec to customize built-in debug profiles."))
|
||||
}
|
||||
|
||||
// Complete finishes run-time initialization of debug.DebugOptions.
|
||||
|
@ -497,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)
|
||||
|
@ -613,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
|
||||
|
@ -626,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) {
|
||||
|
@ -947,12 +986,12 @@ func (o *DebugOptions) handleAttachPod(ctx context.Context, restClientGetter gen
|
|||
}
|
||||
if status.State.Terminated != nil {
|
||||
klog.V(1).Info("Ephemeral container terminated, falling back to logs")
|
||||
return logOpts(restClientGetter, pod, opts)
|
||||
return logOpts(ctx, restClientGetter, pod, opts)
|
||||
}
|
||||
|
||||
if err := opts.Run(); err != nil {
|
||||
fmt.Fprintf(opts.ErrOut, "warning: couldn't attach to pod/%s, falling back to streaming logs: %v\n", podName, err)
|
||||
return logOpts(restClientGetter, pod, opts)
|
||||
return logOpts(ctx, restClientGetter, pod, opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -970,7 +1009,7 @@ func getContainerStatusByName(pod *corev1.Pod, containerName string) *corev1.Con
|
|||
}
|
||||
|
||||
// logOpts logs output from opts to the pods log.
|
||||
func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *corev1.Pod, opts *attach.AttachOptions) error {
|
||||
func logOpts(ctx context.Context, restClientGetter genericclioptions.RESTClientGetter, pod *corev1.Pod, opts *attach.AttachOptions) error {
|
||||
ctrName, err := opts.GetContainerName(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -981,7 +1020,7 @@ func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *corev1.Po
|
|||
return err
|
||||
}
|
||||
for _, request := range requests {
|
||||
if err := logs.DefaultConsumeRequest(request, opts.Out); err != nil {
|
||||
if err := logs.DefaultConsumeRequest(ctx, request, opts.Out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -103,6 +103,6 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.
|
|||
"Defaults to the line ending native to your platform.")
|
||||
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-edit")
|
||||
cmdutil.AddApplyAnnotationVarFlags(cmd, &o.ApplyAnnotation)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, edit will operate on the subresource of the requested object.", editor.SupportedSubresources...)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, edit will operate on the subresource of the requested object.")
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,7 +47,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/interrupt"
|
||||
"k8s.io/kubectl/pkg/util/slice"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
@ -145,8 +144,6 @@ const (
|
|||
useServerPrintColumns = "server-print"
|
||||
)
|
||||
|
||||
var supportedSubresources = []string{"status", "scale"}
|
||||
|
||||
// NewGetOptions returns a GetOptions with default chunk size 500.
|
||||
func NewGetOptions(parent string, streams genericiooptions.IOStreams) *GetOptions {
|
||||
return &GetOptions{
|
||||
|
@ -192,7 +189,7 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericiooptions.IOStre
|
|||
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to get from a server.")
|
||||
cmdutil.AddChunkSizeFlag(cmd, &o.ChunkSize)
|
||||
cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, gets the subresource of the requested object.", supportedSubresources...)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, gets the subresource of the requested object.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -319,9 +316,6 @@ func (o *GetOptions) Validate() error {
|
|||
if o.OutputWatchEvents && !(o.Watch || o.WatchOnly) {
|
||||
return fmt.Errorf("--output-watch-events option can only be used with --watch or --watch-only")
|
||||
}
|
||||
if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) {
|
||||
return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -492,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"
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
"k8s.io/kubectl/pkg/util"
|
||||
"k8s.io/kubectl/pkg/util/completion"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/interrupt"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
|
@ -124,7 +125,7 @@ type LogsOptions struct {
|
|||
Options runtime.Object
|
||||
Resources []string
|
||||
|
||||
ConsumeRequestFn func(rest.ResponseWrapper, io.Writer) error
|
||||
ConsumeRequestFn func(context.Context, rest.ResponseWrapper, io.Writer) error
|
||||
|
||||
// PodLogOptions
|
||||
SinceTime string
|
||||
|
@ -375,14 +376,21 @@ func (o LogsOptions) RunLogs() error {
|
|||
len(requests), o.MaxFollowConcurrency,
|
||||
)
|
||||
}
|
||||
|
||||
return o.parallelConsumeRequest(requests)
|
||||
}
|
||||
|
||||
return o.sequentialConsumeRequest(requests)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
intr := interrupt.New(nil, cancel)
|
||||
return intr.Run(func() error {
|
||||
if o.Follow && len(requests) > 1 {
|
||||
return o.parallelConsumeRequest(ctx, requests)
|
||||
}
|
||||
|
||||
return o.sequentialConsumeRequest(ctx, requests)
|
||||
})
|
||||
}
|
||||
|
||||
func (o LogsOptions) parallelConsumeRequest(requests map[corev1.ObjectReference]rest.ResponseWrapper) error {
|
||||
func (o LogsOptions) parallelConsumeRequest(ctx context.Context, requests map[corev1.ObjectReference]rest.ResponseWrapper) error {
|
||||
reader, writer := io.Pipe()
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(requests))
|
||||
|
@ -390,7 +398,7 @@ func (o LogsOptions) parallelConsumeRequest(requests map[corev1.ObjectReference]
|
|||
go func(objRef corev1.ObjectReference, request rest.ResponseWrapper) {
|
||||
defer wg.Done()
|
||||
out := o.addPrefixIfNeeded(objRef, writer)
|
||||
if err := o.ConsumeRequestFn(request, out); err != nil {
|
||||
if err := o.ConsumeRequestFn(ctx, request, out); err != nil {
|
||||
if !o.IgnoreLogErrors {
|
||||
writer.CloseWithError(err)
|
||||
|
||||
|
@ -413,10 +421,10 @@ func (o LogsOptions) parallelConsumeRequest(requests map[corev1.ObjectReference]
|
|||
return err
|
||||
}
|
||||
|
||||
func (o LogsOptions) sequentialConsumeRequest(requests map[corev1.ObjectReference]rest.ResponseWrapper) error {
|
||||
func (o LogsOptions) sequentialConsumeRequest(ctx context.Context, requests map[corev1.ObjectReference]rest.ResponseWrapper) error {
|
||||
for objRef, request := range requests {
|
||||
out := o.addPrefixIfNeeded(objRef, o.Out)
|
||||
if err := o.ConsumeRequestFn(request, out); err != nil {
|
||||
if err := o.ConsumeRequestFn(ctx, request, out); err != nil {
|
||||
if !o.IgnoreLogErrors {
|
||||
return err
|
||||
}
|
||||
|
@ -457,8 +465,8 @@ func (o LogsOptions) addPrefixIfNeeded(ref corev1.ObjectReference, writer io.Wri
|
|||
// A successful read returns err == nil, not err == io.EOF.
|
||||
// Because the function is defined to read from request until io.EOF, it does
|
||||
// not treat an io.EOF as an error to be reported.
|
||||
func DefaultConsumeRequest(request rest.ResponseWrapper, out io.Writer) error {
|
||||
readCloser, err := request.Stream(context.TODO())
|
||||
func DefaultConsumeRequest(ctx context.Context, request rest.ResponseWrapper, out io.Writer) error {
|
||||
readCloser, err := request.Stream(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -304,7 +304,7 @@ func TestLog(t *testing.T) {
|
|||
|
||||
o := NewLogsOptions(streams)
|
||||
o.LogsForObject = mock.mockLogsForObject
|
||||
o.ConsumeRequestFn = func(req restclient.ResponseWrapper, out io.Writer) error {
|
||||
o.ConsumeRequestFn = func(ctx context.Context, req restclient.ResponseWrapper, out io.Writer) error {
|
||||
return errors.New("Error from the ConsumeRequestFn")
|
||||
}
|
||||
return o
|
||||
|
@ -378,7 +378,7 @@ func TestLog(t *testing.T) {
|
|||
|
||||
o := NewLogsOptions(streams)
|
||||
o.LogsForObject = mock.mockLogsForObject
|
||||
o.ConsumeRequestFn = func(req restclient.ResponseWrapper, out io.Writer) error {
|
||||
o.ConsumeRequestFn = func(ctx context.Context, req restclient.ResponseWrapper, out io.Writer) error {
|
||||
return errors.New("Error from the ConsumeRequestFn")
|
||||
}
|
||||
o.Follow = true
|
||||
|
@ -401,7 +401,7 @@ func TestLog(t *testing.T) {
|
|||
|
||||
o := NewLogsOptions(streams)
|
||||
o.LogsForObject = mock.mockLogsForObject
|
||||
o.ConsumeRequestFn = func(req restclient.ResponseWrapper, out io.Writer) error {
|
||||
o.ConsumeRequestFn = func(ctx context.Context, req restclient.ResponseWrapper, out io.Writer) error {
|
||||
return errors.New("Error from the ConsumeRequestFn")
|
||||
}
|
||||
o.Follow = true
|
||||
|
@ -808,7 +808,7 @@ func TestDefaultConsumeRequest(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
err := DefaultConsumeRequest(test.request, buf)
|
||||
err := DefaultConsumeRequest(context.TODO(), test.request, buf)
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), test.expectedErr) {
|
||||
t.Errorf("%s: expected to find:\n\t%s\nfound:\n\t%s\n", test.name, test.expectedErr, err.Error())
|
||||
|
@ -932,8 +932,8 @@ type logTestMock struct {
|
|||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func (l *logTestMock) mockConsumeRequest(request restclient.ResponseWrapper, out io.Writer) error {
|
||||
readCloser, err := request.Stream(context.Background())
|
||||
func (l *logTestMock) mockConsumeRequest(ctx context.Context, request restclient.ResponseWrapper, out io.Writer) error {
|
||||
readCloser, err := request.Stream(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -45,7 +44,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util/completion"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/slice"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
|
@ -107,8 +105,6 @@ var (
|
|||
kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":2}}'`))
|
||||
)
|
||||
|
||||
var supportedSubresources = []string{"status", "scale"}
|
||||
|
||||
func NewPatchOptions(ioStreams genericiooptions.IOStreams) *PatchOptions {
|
||||
return &PatchOptions{
|
||||
RecordFlags: genericclioptions.NewRecordFlags(),
|
||||
|
@ -140,12 +136,12 @@ 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.")
|
||||
cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-patch")
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, patch will operate on the subresource of the requested object.", supportedSubresources...)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, patch will operate on the subresource of the requested object.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -197,12 +193,9 @@ 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)
|
||||
}
|
||||
}
|
||||
if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) {
|
||||
return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -266,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
|
||||
}
|
||||
|
|
|
@ -223,7 +223,9 @@ func convertPodNamedPortToNumber(ports []string, pod corev1.Pod) ([]string, erro
|
|||
var converted []string
|
||||
for _, port := range ports {
|
||||
localPort, remotePort := splitPort(port)
|
||||
|
||||
if remotePort == "" {
|
||||
return nil, fmt.Errorf("remote port cannot be empty")
|
||||
}
|
||||
containerPortStr := remotePort
|
||||
_, err := strconv.Atoi(remotePort)
|
||||
if err != nil {
|
||||
|
@ -245,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)
|
||||
|
@ -279,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 {
|
||||
|
@ -296,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)
|
||||
|
|
|
@ -820,6 +820,24 @@ func TestConvertPodNamedPortToNumber(t *testing.T) {
|
|||
ports: []string{"https", "http"},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "empty port name",
|
||||
pod: corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: int32(27017)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ports: []string{"28015:"},
|
||||
converted: nil,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package replace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -40,7 +41,6 @@ import (
|
|||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/slice"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
"k8s.io/kubectl/pkg/validation"
|
||||
)
|
||||
|
@ -68,8 +68,6 @@ var (
|
|||
kubectl replace --force -f ./pod.json`))
|
||||
)
|
||||
|
||||
var supportedSubresources = []string{"status", "scale"}
|
||||
|
||||
type ReplaceOptions struct {
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
RecordFlags *genericclioptions.RecordFlags
|
||||
|
@ -136,7 +134,7 @@ func NewCmdReplace(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra
|
|||
|
||||
cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to PUT to the server. Uses the transport specified by the kubeconfig file.")
|
||||
cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-replace")
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, replace will operate on the subresource of the requested object.", supportedSubresources...)
|
||||
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, replace will operate on the subresource of the requested object.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -249,10 +247,6 @@ func (o *ReplaceOptions) Validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) {
|
||||
return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -365,8 +359,7 @@ func (o *ReplaceOptions) forceReplace() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return wait.PollImmediate(1*time.Second, timeout, func() (bool, error) {
|
||||
return wait.PollUntilContextTimeout(context.Background(), 1*time.Second, timeout, true, func(ctx context.Context) (bool, error) {
|
||||
if err := info.Get(); !errors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -504,7 +504,7 @@ func logOpts(restClientGetter genericclioptions.RESTClientGetter, pod *corev1.Po
|
|||
return err
|
||||
}
|
||||
for _, request := range requests {
|
||||
if err := logs.DefaultConsumeRequest(request, opts.Out); err != nil {
|
||||
if err := logs.DefaultConsumeRequest(context.Background(), request, opts.Out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -52,11 +52,8 @@ import (
|
|||
"k8s.io/kubectl/pkg/cmd/util/editor/crlf"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
"k8s.io/kubectl/pkg/util"
|
||||
"k8s.io/kubectl/pkg/util/slice"
|
||||
)
|
||||
|
||||
var SupportedSubresources = []string{"status"}
|
||||
|
||||
// EditOptions contains all the options for running edit cli command.
|
||||
type EditOptions struct {
|
||||
resource.FilenameOptions
|
||||
|
@ -230,9 +227,6 @@ func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Comm
|
|||
|
||||
// Validate checks the EditOptions to see if there is sufficient information to run the command.
|
||||
func (o *EditOptions) Validate() error {
|
||||
if len(o.Subresource) > 0 && !slice.ContainsString(SupportedSubresources, o.Subresource, nil) {
|
||||
return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, SupportedSubresources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -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,12 +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 FeatureGate = "KUBECTL_DEBUG_CUSTOM_PROFILE"
|
||||
)
|
||||
|
||||
// IsEnabled returns true iff environment variable is set to true.
|
||||
|
@ -518,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) {
|
||||
|
@ -536,10 +560,11 @@ func AddPruningFlags(cmd *cobra.Command, prune *bool, pruneAllowlist *[]string,
|
|||
}
|
||||
}
|
||||
|
||||
func AddSubresourceFlags(cmd *cobra.Command, subresource *string, usage string, allowedSubresources ...string) {
|
||||
cmd.Flags().StringVar(subresource, "subresource", "", fmt.Sprintf("%s Must be one of %v. This flag is beta and may change in the future.", usage, allowedSubresources))
|
||||
func AddSubresourceFlags(cmd *cobra.Command, subresource *string, usage string) {
|
||||
cmd.Flags().StringVar(subresource, "subresource", "", usage)
|
||||
CheckErr(cmd.RegisterFlagCompletionFunc("subresource", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
|
||||
return allowedSubresources, cobra.ShellCompDirectiveNoFileComp
|
||||
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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue