Compare commits
27 Commits
v0.34.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
2a6e583a42 | |
|
9411a867c0 | |
|
aa186336f3 | |
|
4dec168032 | |
|
48317edcc3 | |
|
9f67d52cb6 | |
|
33f48403eb | |
|
ceae52f082 | |
|
74f9adbfa6 | |
|
a628f84f45 | |
|
e98b7e433d | |
|
cce124a8d8 | |
|
9e0a26615e | |
|
4ce6135c24 | |
|
22d999d2fc | |
|
0889899212 | |
|
f4a1e23b35 | |
|
3c19b8deb3 | |
|
cbe60c952e | |
|
12dc58ebf7 | |
|
7b4e1d1699 | |
|
4b52eef334 | |
|
ce4d90902a | |
|
21b32eea57 | |
|
403b4a41e8 | |
|
a7bf48f663 | |
|
5c587a03ba |
33
go.mod
33
go.mod
|
@ -27,17 +27,17 @@ require (
|
||||||
github.com/spf13/pflag v1.0.6
|
github.com/spf13/pflag v1.0.6
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
go.yaml.in/yaml/v2 v2.4.2
|
go.yaml.in/yaml/v2 v2.4.2
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.33.0
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0
|
gopkg.in/evanphx/json-patch.v4 v4.13.0
|
||||||
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a
|
||||||
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4
|
||||||
k8s.io/cli-runtime v0.0.0-20250725032234-4ef9d6338b92
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e
|
||||||
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97
|
||||||
k8s.io/component-base v0.0.0-20250725025923-b9f1c2d98961
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7
|
||||||
k8s.io/component-helpers v0.0.0-20250725030049-f50e498bf4cb
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc
|
||||||
k8s.io/klog/v2 v2.130.1
|
k8s.io/klog/v2 v2.130.1
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||||
k8s.io/metrics v0.0.0-20250725032103-4d7234291ae2
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8
|
||||||
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1
|
sigs.k8s.io/kustomize/kustomize/v5 v5.7.1
|
||||||
|
@ -54,7 +54,7 @@ require (
|
||||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.2 // indirect
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
@ -76,19 +76,18 @@ require (
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/xlab/treeprint v1.2.0 // indirect
|
github.com/xlab/treeprint v1.2.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/net v0.38.0 // indirect
|
golang.org/x/net v0.40.0 // indirect
|
||||||
golang.org/x/oauth2 v0.27.0 // indirect
|
golang.org/x/oauth2 v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.12.0 // indirect
|
golang.org/x/sync v0.14.0 // indirect
|
||||||
golang.org/x/term v0.30.0 // indirect
|
golang.org/x/term v0.32.0 // indirect
|
||||||
golang.org/x/text v0.23.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
golang.org/x/time v0.9.0 // indirect
|
golang.org/x/time v0.9.0 // indirect
|
||||||
golang.org/x/tools v0.26.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
sigs.k8s.io/kustomize/api v0.20.1 // indirect
|
sigs.k8s.io/kustomize/api v0.20.1 // indirect
|
||||||
|
|
66
go.sum
66
go.sum
|
@ -27,8 +27,8 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa
|
||||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
@ -105,8 +105,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
|
@ -153,27 +151,27 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -186,36 +184,36 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d h1:OdC1L69BYvh/4HX6Wgg7DeL0A++gD3gadKfaWT4IdbA=
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a h1:qS+abmAu2zbGFkbN1vA7LKS07jsXBN1BvTFXFvaGOLI=
|
||||||
k8s.io/api v0.0.0-20250725024535-b95b43d5b95d/go.mod h1:wKZv1VB6nzJ6L449TteVelrBfRsawhrthiOsEylKo8U=
|
k8s.io/api v0.0.0-20250830163657-b903cd06836a/go.mod h1:/IpJMZ4ur2JBuX+kkBc115bnq09sFfUnbuFNrdEe5yc=
|
||||||
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4 h1:N25HX4lRPTvLHSUPoCMFP+B/oEcOmPESB+BRkYMD8Io=
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4 h1:ObQoOWhkcPbMnU7PIHT2pkO2wK66CcBn6vD+77CidHM=
|
||||||
k8s.io/apimachinery v0.0.0-20250725024258-04507a37f6a4/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
k8s.io/apimachinery v0.0.0-20250830163350-eb2c6e0d1ec4/go.mod h1:fDax9lidUgmNSmBlzUrSISURQmHpeyamBbKX9jGbJ3g=
|
||||||
k8s.io/cli-runtime v0.0.0-20250725032234-4ef9d6338b92 h1:RdOB0Lh+NyJmf7AonSc+L7yqokyDAXJ81qmSs0TtHUw=
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e h1:dovWIA2PM0UrJrZdUBV8uy5pExliSBXSFeL0bI6IX6E=
|
||||||
k8s.io/cli-runtime v0.0.0-20250725032234-4ef9d6338b92/go.mod h1:1tVpyR8K/bn6FWi+8E38u97eWqhBRZb07NprI1o/rdY=
|
k8s.io/cli-runtime v0.0.0-20250830171832-3e7914c55f7e/go.mod h1:/yp9r2rD6AV7MYM/gmb55/6LttRuURzjhgqbfiFQ0Rg=
|
||||||
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d h1:8i9q3fd352G8FsurDLj4++5oMS4gWqZ5O2lErveG9Ok=
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97 h1:6ks7Y8CNm05xZ6eyE0db5IDP54PIyRM3aZhpflG55hI=
|
||||||
k8s.io/client-go v0.0.0-20250725024918-f78361a6474d/go.mod h1:j4aKw1XACZSFUnRBqWNU0d3TXbXzaBhAbazCrGTYHdg=
|
k8s.io/client-go v0.0.0-20250830164107-2a8d855d0d97/go.mod h1:xTpANYjBhGsmpO7Gdw8kMt3yQfciVwyRhbqcq77qwyI=
|
||||||
k8s.io/component-base v0.0.0-20250725025923-b9f1c2d98961 h1:gWZcTfCQXU9F1Yqzs0dRUxDKI6fxb+1hqrzd8XAP8y0=
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7 h1:JQ/dO+EIXNQ+y3Vlez6PC6r7T+0JvNQWrBx6CD3jKls=
|
||||||
k8s.io/component-base v0.0.0-20250725025923-b9f1c2d98961/go.mod h1:+EKWDhmrFYENWq8vZZtJ9DvxEebCdVtSK4MGTw6CDuE=
|
k8s.io/component-base v0.0.0-20250830165319-75dce96cfea7/go.mod h1:ZZMk2BFRSF/kI9Y5qKmvTk4SJM654XsQ5eJ9cP7mrhw=
|
||||||
k8s.io/component-helpers v0.0.0-20250725030049-f50e498bf4cb h1:o6hsldkBW9pDOMkRD83FCzIhV3umWyniqr3jfoTwRhA=
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc h1:hEo6RVciD0e0QvtMBgwG9a7fFWb/vkx0Jvw/iQ5i+lQ=
|
||||||
k8s.io/component-helpers v0.0.0-20250725030049-f50e498bf4cb/go.mod h1:2GQI4QDHdIY75ammFd3kBExbETgetCR7ZOGEuZtwAgE=
|
k8s.io/component-helpers v0.0.0-20250830165448-f84446610ecc/go.mod h1:TmnJ20kJrkgbEqHgHoUxoTksgIUJNCWtv/QM+yBqCF0=
|
||||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||||
k8s.io/metrics v0.0.0-20250725032103-4d7234291ae2 h1:ClXmZHyqm8xtoVhj5E4au3TaqDxEuC2EqDoUouJvVC4=
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd h1:GNGeQ8o/xw8TwhavcXmWrGKHd0ez5TRx8qRqyncbFH4=
|
||||||
k8s.io/metrics v0.0.0-20250725032103-4d7234291ae2/go.mod h1:OOYryEbeiszIzWX/KP2G8+HFXQURdZkIODzfc7Gjwsc=
|
k8s.io/metrics v0.0.0-20250830171643-3daf20f585bd/go.mod h1:OYYW2zJf2TAQHmxQwQgSsP/i40dCZig4RQkw3o3vFPE=
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||||
|
|
|
@ -18,6 +18,7 @@ package apiresources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -57,8 +58,8 @@ See 'kubectl api-resources -h' for help and examples`
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("An error was expected but not returned")
|
t.Fatalf("An error was expected but not returned")
|
||||||
}
|
}
|
||||||
expectedError = `unable to match a printer suitable for the output format "foo", allowed formats are: json,name,wide,yaml`
|
expectedError = `unable to match a printer suitable for the output format "foo", allowed formats are:`
|
||||||
if err.Error() != expectedError {
|
if !strings.HasPrefix(err.Error(), expectedError) {
|
||||||
t.Fatalf("Unexpected error: %v\n expected: %v", err, expectedError)
|
t.Fatalf("Unexpected error: %v\n expected: %v", err, expectedError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"k8s.io/kubectl/pkg/util/completion"
|
"k8s.io/kubectl/pkg/util/completion"
|
||||||
"k8s.io/kubectl/pkg/util/i18n"
|
"k8s.io/kubectl/pkg/util/i18n"
|
||||||
"k8s.io/kubectl/pkg/util/templates"
|
"k8s.io/kubectl/pkg/util/templates"
|
||||||
|
"k8s.io/kubectl/pkg/util/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -300,7 +301,9 @@ func (o *AttachOptions) Run() error {
|
||||||
sizePlusOne.Height++
|
sizePlusOne.Height++
|
||||||
|
|
||||||
// this call spawns a goroutine to monitor/update the terminal size
|
// this call spawns a goroutine to monitor/update the terminal size
|
||||||
sizeQueue = t.MonitorSize(&sizePlusOne, size)
|
sizeQueue = &terminalSizeQueueAdapter{
|
||||||
|
delegate: t.MonitorSize(&sizePlusOne, size),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.DisableStderr = true
|
o.DisableStderr = true
|
||||||
|
@ -357,3 +360,18 @@ func (o *AttachOptions) reattachMessage(containerName string, rawTTY bool) strin
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Session ended, resume using '%s %s -c %s -i -t' command when the pod is running", o.CommandName, o.Pod.Name, containerName)
|
return fmt.Sprintf("Session ended, resume using '%s %s -c %s -i -t' command when the pod is running", o.CommandName, o.Pod.Name, containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type terminalSizeQueueAdapter struct {
|
||||||
|
delegate term.TerminalSizeQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *terminalSizeQueueAdapter) Next() *remotecommand.TerminalSize {
|
||||||
|
next := a.delegate.Next()
|
||||||
|
if next == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &remotecommand.TerminalSize{
|
||||||
|
Width: next.Width,
|
||||||
|
Height: next.Height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
@ -33,6 +34,7 @@ import (
|
||||||
"k8s.io/cli-runtime/pkg/resource"
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
rbacv1client "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
||||||
|
"k8s.io/client-go/util/retry"
|
||||||
"k8s.io/component-helpers/auth/rbac/reconciliation"
|
"k8s.io/component-helpers/auth/rbac/reconciliation"
|
||||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/scheme"
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
|
@ -197,6 +199,9 @@ func (o *ReconcileOptions) Validate() error {
|
||||||
|
|
||||||
// RunReconcile performs the execution
|
// RunReconcile performs the execution
|
||||||
func (o *ReconcileOptions) RunReconcile() error {
|
func (o *ReconcileOptions) RunReconcile() error {
|
||||||
|
// conflictBackoff retries up to 3 times on conflict, with no delay
|
||||||
|
conflictBackoff := wait.Backoff{Steps: 3}
|
||||||
|
|
||||||
return o.Visitor.Visit(func(info *resource.Info, err error) error {
|
return o.Visitor.Visit(func(info *resource.Info, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -213,7 +218,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient,
|
Client: o.RBACClient,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
|
result *reconciliation.ReconcileClusterRoleResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -228,7 +240,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient.ClusterRoles(),
|
Client: o.RBACClient.ClusterRoles(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
|
result *reconciliation.ReconcileClusterRoleResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -244,10 +263,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
NamespaceClient: o.NamespaceClient.Namespaces(),
|
NamespaceClient: o.NamespaceClient.Namespaces(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
if err != nil {
|
result *reconciliation.ReconcileClusterRoleBindingResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
||||||
|
|
||||||
case *rbacv1.ClusterRoleBinding:
|
case *rbacv1.ClusterRoleBinding:
|
||||||
|
@ -259,10 +282,14 @@ func (o *ReconcileOptions) RunReconcile() error {
|
||||||
Client: o.RBACClient.ClusterRoleBindings(),
|
Client: o.RBACClient.ClusterRoleBindings(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
result, err := reconcileOptions.Run()
|
var (
|
||||||
if err != nil {
|
result *reconciliation.ReconcileClusterRoleBindingResult
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
retry.RetryOnConflict(conflictBackoff, func() error {
|
||||||
|
result, err = reconcileOptions.Run()
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
o.printResults(result.RoleBinding.GetObject(), result.MissingSubjects, result.ExtraSubjects, nil, nil, result.Operation, result.Protected)
|
||||||
|
|
||||||
case *rbacv1beta1.Role,
|
case *rbacv1beta1.Role,
|
||||||
|
|
|
@ -129,7 +129,8 @@ func NewCmdConfigSetCredentials(out io.Writer, configAccess clientcmd.ConfigAcce
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCmdConfigSetAuthInfo returns a Command instance for 'config set-credentials' sub command
|
// NewCmdConfigSetAuthInfo returns a Command instance for 'config set-credentials' sub command
|
||||||
// DEPRECATED: Use NewCmdConfigSetCredentials instead
|
//
|
||||||
|
// Deprecated: Use NewCmdConfigSetCredentials instead
|
||||||
func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
|
||||||
return NewCmdConfigSetCredentials(out, configAccess)
|
return NewCmdConfigSetCredentials(out, configAccess)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *c
|
||||||
cmdutil.AddValidateFlags(cmd)
|
cmdutil.AddValidateFlags(cmd)
|
||||||
cmdutil.AddDryRunFlag(cmd)
|
cmdutil.AddDryRunFlag(cmd)
|
||||||
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
|
cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
|
||||||
cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")
|
cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only CronJob is supported).")
|
||||||
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
|
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package drain
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
@ -151,10 +152,11 @@ func NewDrainCmdOptions(f cmdutil.Factory, ioStreams genericiooptions.IOStreams)
|
||||||
PrintFlags: genericclioptions.NewPrintFlags("drained").WithTypeSetter(scheme.Scheme),
|
PrintFlags: genericclioptions.NewPrintFlags("drained").WithTypeSetter(scheme.Scheme),
|
||||||
IOStreams: ioStreams,
|
IOStreams: ioStreams,
|
||||||
drainer: &drain.Helper{
|
drainer: &drain.Helper{
|
||||||
GracePeriodSeconds: -1,
|
GracePeriodSeconds: -1,
|
||||||
Out: ioStreams.Out,
|
EvictErrorRetryDelay: 5 * time.Second,
|
||||||
ErrOut: ioStreams.ErrOut,
|
Out: ioStreams.Out,
|
||||||
ChunkSize: cmdutil.DefaultChunkSize,
|
ErrOut: ioStreams.ErrOut,
|
||||||
|
ChunkSize: cmdutil.DefaultChunkSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
o.drainer.OnPodDeletionOrEvictionFinished = o.onPodDeletionOrEvictionFinished
|
o.drainer.OnPodDeletionOrEvictionFinished = o.onPodDeletionOrEvictionFinished
|
||||||
|
|
|
@ -366,7 +366,9 @@ func (p *ExecOptions) Run() error {
|
||||||
var sizeQueue remotecommand.TerminalSizeQueue
|
var sizeQueue remotecommand.TerminalSizeQueue
|
||||||
if t.Raw {
|
if t.Raw {
|
||||||
// this call spawns a goroutine to monitor/update the terminal size
|
// this call spawns a goroutine to monitor/update the terminal size
|
||||||
sizeQueue = t.MonitorSize(t.GetSize())
|
sizeQueue = &terminalSizeQueueAdapter{
|
||||||
|
delegate: t.MonitorSize(t.GetSize()),
|
||||||
|
}
|
||||||
|
|
||||||
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
|
// unset p.Err if it was previously set because both stdout and stderr go over p.Out when tty is
|
||||||
// true
|
// true
|
||||||
|
@ -403,3 +405,18 @@ func (p *ExecOptions) Run() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type terminalSizeQueueAdapter struct {
|
||||||
|
delegate term.TerminalSizeQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *terminalSizeQueueAdapter) Next() *remotecommand.TerminalSize {
|
||||||
|
next := a.delegate.Next()
|
||||||
|
if next == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &remotecommand.TerminalSize{
|
||||||
|
Width: next.Width,
|
||||||
|
Height: next.Height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -206,7 +206,7 @@ func (flags *ExposeServiceFlags) AddFlags(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringVar(&flags.Port, "port", flags.Port, i18n.T("The port that the service should serve on. Copied from the resource being exposed, if unspecified"))
|
cmd.Flags().StringVar(&flags.Port, "port", flags.Port, i18n.T("The port that the service should serve on. Copied from the resource being exposed, if unspecified"))
|
||||||
cmd.Flags().StringVar(&flags.Type, "type", flags.Type, i18n.T("Type for this service: ClusterIP, NodePort, LoadBalancer, or ExternalName. Default is 'ClusterIP'."))
|
cmd.Flags().StringVar(&flags.Type, "type", flags.Type, i18n.T("Type for this service: ClusterIP, NodePort, LoadBalancer, or ExternalName. Default is 'ClusterIP'."))
|
||||||
cmd.Flags().StringVar(&flags.LoadBalancerIP, "load-balancer-ip", flags.LoadBalancerIP, i18n.T("IP to assign to the LoadBalancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)."))
|
cmd.Flags().StringVar(&flags.LoadBalancerIP, "load-balancer-ip", flags.LoadBalancerIP, i18n.T("IP to assign to the LoadBalancer. If empty, an ephemeral IP will be created and used (cloud-provider specific)."))
|
||||||
cmd.Flags().StringVar(&flags.Selector, "selector", flags.Selector, i18n.T("A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.)"))
|
cmd.Flags().StringVar(&flags.Selector, "selector", flags.Selector, i18n.T("A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the resource being exposed."))
|
||||||
cmd.Flags().StringVarP(&flags.Labels, "labels", "l", flags.Labels, "Labels to apply to the service created by this call.")
|
cmd.Flags().StringVarP(&flags.Labels, "labels", "l", flags.Labels, "Labels to apply to the service created by this call.")
|
||||||
cmd.Flags().StringVar(&flags.TargetPort, "target-port", flags.TargetPort, i18n.T("Name or number for the port on the container that the service should direct traffic to. Optional."))
|
cmd.Flags().StringVar(&flags.TargetPort, "target-port", flags.TargetPort, i18n.T("Name or number for the port on the container that the service should direct traffic to. Optional."))
|
||||||
cmd.Flags().StringVar(&flags.ExternalIP, "external-ip", flags.ExternalIP, i18n.T("Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP."))
|
cmd.Flags().StringVar(&flags.ExternalIP, "external-ip", flags.ExternalIP, i18n.T("Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP."))
|
||||||
|
|
|
@ -74,7 +74,7 @@ var (
|
||||||
selectorExample = templates.Examples(`
|
selectorExample = templates.Examples(`
|
||||||
# Set the labels and selector before creating a deployment/service pair
|
# Set the labels and selector before creating a deployment/service pair
|
||||||
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
|
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
|
||||||
kubectl create deployment my-dep -o yaml --dry-run=client | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
|
kubectl create deployment my-dep --image=nginx -o yaml --dry-run=client | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewSelectorOptions returns an initialized SelectorOptions instance
|
// NewSelectorOptions returns an initialized SelectorOptions instance
|
||||||
|
|
|
@ -46,7 +46,7 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
waitLong = templates.LongDesc(i18n.T(`
|
waitLong = templates.LongDesc(i18n.T(`
|
||||||
Experimental: Wait for a specific condition on one or many resources.
|
Wait for a specific condition on one or many resources.
|
||||||
|
|
||||||
The command takes multiple resources and waits until the specified condition
|
The command takes multiple resources and waits until the specified condition
|
||||||
is seen in the Status field of every given resource.
|
is seen in the Status field of every given resource.
|
||||||
|
|
|
@ -3445,61 +3445,15 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens := []corev1.Secret{}
|
|
||||||
|
|
||||||
// missingSecrets is the set of all secrets present in the
|
|
||||||
// serviceAccount but not present in the set of existing secrets.
|
|
||||||
missingSecrets := sets.New[string]()
|
|
||||||
secrets := corev1.SecretList{}
|
|
||||||
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
|
|
||||||
func(options metav1.ListOptions) (runtime.Object, error) {
|
|
||||||
newList, err := d.CoreV1().Secrets(namespace).List(context.TODO(), options)
|
|
||||||
if err != nil {
|
|
||||||
return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceSecrets.String())
|
|
||||||
}
|
|
||||||
secrets.Items = append(secrets.Items, newList.Items...)
|
|
||||||
return newList, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// errors are tolerated here in order to describe the serviceAccount with all
|
|
||||||
// of the secrets that it references, even if those secrets cannot be fetched.
|
|
||||||
if err == nil {
|
|
||||||
// existingSecrets is the set of all secrets remaining on a
|
|
||||||
// service account that are not present in the "tokens" slice.
|
|
||||||
existingSecrets := sets.New[string]()
|
|
||||||
|
|
||||||
for _, s := range secrets.Items {
|
|
||||||
if s.Type == corev1.SecretTypeServiceAccountToken {
|
|
||||||
name := s.Annotations[corev1.ServiceAccountNameKey]
|
|
||||||
uid := s.Annotations[corev1.ServiceAccountUIDKey]
|
|
||||||
if name == serviceAccount.Name && uid == string(serviceAccount.UID) {
|
|
||||||
tokens = append(tokens, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
existingSecrets.Insert(s.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range serviceAccount.Secrets {
|
|
||||||
if !existingSecrets.Has(s.Name) {
|
|
||||||
missingSecrets.Insert(s.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, s := range serviceAccount.ImagePullSecrets {
|
|
||||||
if !existingSecrets.Has(s.Name) {
|
|
||||||
missingSecrets.Insert(s.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var events *corev1.EventList
|
var events *corev1.EventList
|
||||||
if describerSettings.ShowEvents {
|
if describerSettings.ShowEvents {
|
||||||
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
|
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
|
return describeServiceAccount(serviceAccount, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []corev1.Secret, missingSecrets sets.Set[string], events *corev1.EventList) (string, error) {
|
func describeServiceAccount(serviceAccount *corev1.ServiceAccount, events *corev1.EventList) (string, error) {
|
||||||
return tabbedString(func(out io.Writer) error {
|
return tabbedString(func(out io.Writer) error {
|
||||||
w := NewPrefixWriter(out)
|
w := NewPrefixWriter(out)
|
||||||
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
|
w.Write(LEVEL_0, "Name:\t%s\n", serviceAccount.Name)
|
||||||
|
@ -3510,28 +3464,16 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []core
|
||||||
var (
|
var (
|
||||||
emptyHeader = " "
|
emptyHeader = " "
|
||||||
pullHeader = "Image pull secrets:"
|
pullHeader = "Image pull secrets:"
|
||||||
mountHeader = "Mountable secrets: "
|
|
||||||
tokenHeader = "Tokens: "
|
|
||||||
|
|
||||||
pullSecretNames = []string{}
|
pullSecretNames = []string{}
|
||||||
mountSecretNames = []string{}
|
|
||||||
tokenSecretNames = []string{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, s := range serviceAccount.ImagePullSecrets {
|
for _, s := range serviceAccount.ImagePullSecrets {
|
||||||
pullSecretNames = append(pullSecretNames, s.Name)
|
pullSecretNames = append(pullSecretNames, s.Name)
|
||||||
}
|
}
|
||||||
for _, s := range serviceAccount.Secrets {
|
|
||||||
mountSecretNames = append(mountSecretNames, s.Name)
|
|
||||||
}
|
|
||||||
for _, s := range tokens {
|
|
||||||
tokenSecretNames = append(tokenSecretNames, s.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
types := map[string][]string{
|
types := map[string][]string{
|
||||||
pullHeader: pullSecretNames,
|
pullHeader: pullSecretNames,
|
||||||
mountHeader: mountSecretNames,
|
|
||||||
tokenHeader: tokenSecretNames,
|
|
||||||
}
|
}
|
||||||
for _, header := range sets.List(sets.KeySet(types)) {
|
for _, header := range sets.List(sets.KeySet(types)) {
|
||||||
names := types[header]
|
names := types[header]
|
||||||
|
@ -3540,11 +3482,7 @@ func describeServiceAccount(serviceAccount *corev1.ServiceAccount, tokens []core
|
||||||
} else {
|
} else {
|
||||||
prefix := header
|
prefix := header
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if missingSecrets.Has(name) {
|
w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
|
||||||
w.Write(LEVEL_0, "%s\t%s (not found)\n", prefix, name)
|
|
||||||
} else {
|
|
||||||
w.Write(LEVEL_0, "%s\t%s\n", prefix, name)
|
|
||||||
}
|
|
||||||
prefix = emptyHeader
|
prefix = emptyHeader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6238,9 +6238,7 @@ func TestDescribeServiceAccount(t *testing.T) {
|
||||||
Namespace: foo
|
Namespace: foo
|
||||||
Labels: <none>
|
Labels: <none>
|
||||||
Annotations: <none>
|
Annotations: <none>
|
||||||
Image pull secrets: test-local-ref (not found)
|
Image pull secrets: test-local-ref
|
||||||
Mountable secrets: test-objectref (not found)
|
|
||||||
Tokens: <none>
|
|
||||||
Events: <none>` + "\n"
|
Events: <none>` + "\n"
|
||||||
if out != expectedOut {
|
if out != expectedOut {
|
||||||
t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)
|
t.Errorf("expected : %q\n but got output:\n %q", expectedOut, out)
|
||||||
|
|
|
@ -74,6 +74,9 @@ type Helper struct {
|
||||||
// won't drain otherwise
|
// won't drain otherwise
|
||||||
SkipWaitForDeleteTimeoutSeconds int
|
SkipWaitForDeleteTimeoutSeconds int
|
||||||
|
|
||||||
|
// EvictErrorRetryDelay is used to control the retry delay after a pod eviction error
|
||||||
|
EvictErrorRetryDelay time.Duration
|
||||||
|
|
||||||
// AdditionalFilters are applied sequentially after base drain filters to
|
// AdditionalFilters are applied sequentially after base drain filters to
|
||||||
// exclude pods using custom logic. Any filter that returns PodDeleteStatus
|
// exclude pods using custom logic. Any filter that returns PodDeleteStatus
|
||||||
// with Delete == false will immediately stop execution of further filters.
|
// with Delete == false will immediately stop execution of further filters.
|
||||||
|
@ -278,37 +281,27 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
go func(pod corev1.Pod, returnCh chan error) {
|
go func(pod corev1.Pod, returnCh chan error) {
|
||||||
refreshPod := false
|
activePod := pod
|
||||||
for {
|
for {
|
||||||
switch d.DryRunStrategy {
|
switch d.DryRunStrategy {
|
||||||
case cmdutil.DryRunServer:
|
case cmdutil.DryRunServer:
|
||||||
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", pod.Namespace, pod.Name)
|
//nolint:errcheck
|
||||||
|
fmt.Fprintf(d.Out, "evicting pod %s/%s (server dry run)\n", activePod.Namespace, activePod.Name)
|
||||||
default:
|
default:
|
||||||
if d.OnPodDeletionOrEvictionStarted != nil {
|
if d.OnPodDeletionOrEvictionStarted != nil {
|
||||||
d.OnPodDeletionOrEvictionStarted(&pod, true)
|
d.OnPodDeletionOrEvictionStarted(&activePod, true)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", pod.Namespace, pod.Name)
|
//nolint:errcheck
|
||||||
|
fmt.Fprintf(d.Out, "evicting pod %s/%s\n", activePod.Namespace, activePod.Name)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// return here or we'll leak a goroutine.
|
// return here or we'll leak a goroutine.
|
||||||
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", pod.Name, pod.Namespace, globalTimeout)
|
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", activePod.Name, activePod.Namespace, globalTimeout)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary pod so we don't mutate the pod in the loop.
|
|
||||||
activePod := pod
|
|
||||||
if refreshPod {
|
|
||||||
freshPod, err := getPodFn(pod.Namespace, pod.Name)
|
|
||||||
// We ignore errors and let eviction sort it out with
|
|
||||||
// the original pod.
|
|
||||||
if err == nil {
|
|
||||||
activePod = *freshPod
|
|
||||||
}
|
|
||||||
refreshPod = false
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.EvictPod(activePod, evictionGroupVersion)
|
err := d.EvictPod(activePod, evictionGroupVersion)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
|
@ -316,8 +309,9 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
returnCh <- nil
|
returnCh <- nil
|
||||||
return
|
return
|
||||||
} else if apierrors.IsTooManyRequests(err) {
|
} else if apierrors.IsTooManyRequests(err) {
|
||||||
fmt.Fprintf(d.ErrOut, "error when evicting pods/%q -n %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
|
//nolint:errcheck
|
||||||
time.Sleep(5 * time.Second)
|
fmt.Fprintf(d.ErrOut, "error when evicting pods/%q -n %q (will retry after %v): %v\n", activePod.Name, activePod.Namespace, d.EvictErrorRetryDelay, err)
|
||||||
|
time.Sleep(d.EvictErrorRetryDelay)
|
||||||
} else if !activePod.ObjectMeta.DeletionTimestamp.IsZero() && apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
} else if !activePod.ObjectMeta.DeletionTimestamp.IsZero() && apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
||||||
// an eviction request in a deleting namespace will throw a forbidden error,
|
// an eviction request in a deleting namespace will throw a forbidden error,
|
||||||
// if the pod is already marked deleted, we can ignore this error, an eviction
|
// if the pod is already marked deleted, we can ignore this error, an eviction
|
||||||
|
@ -326,12 +320,19 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
} else if apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
} else if apierrors.IsForbidden(err) && apierrors.HasStatusCause(err, corev1.NamespaceTerminatingCause) {
|
||||||
// an eviction request in a deleting namespace will throw a forbidden error,
|
// an eviction request in a deleting namespace will throw a forbidden error,
|
||||||
// if the pod is not marked deleted, we retry until it is.
|
// if the pod is not marked deleted, we retry until it is.
|
||||||
fmt.Fprintf(d.ErrOut, "error when evicting pod %q from terminating namespace %q (will retry after 5s): %v\n", activePod.Name, activePod.Namespace, err)
|
//nolint:errcheck
|
||||||
time.Sleep(5 * time.Second)
|
fmt.Fprintf(d.ErrOut, "error when evicting pod %q from terminating namespace %q (will retry after %v): %v\n", activePod.Name, activePod.Namespace, d.EvictErrorRetryDelay, err)
|
||||||
|
time.Sleep(d.EvictErrorRetryDelay)
|
||||||
} else {
|
} else {
|
||||||
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: %v", activePod.Name, activePod.Namespace, err)
|
returnCh <- fmt.Errorf("error when evicting pods/%q -n %q: %v", activePod.Name, activePod.Namespace, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
freshPod, err := getPodFn(activePod.Namespace, activePod.Name)
|
||||||
|
// we ignore errors and let eviction sort it out with the original pod.
|
||||||
|
if err == nil {
|
||||||
|
activePod = *freshPod
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if d.DryRunStrategy == cmdutil.DryRunServer {
|
if d.DryRunStrategy == cmdutil.DryRunServer {
|
||||||
returnCh <- nil
|
returnCh <- nil
|
||||||
|
@ -339,7 +340,7 @@ func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupV
|
||||||
}
|
}
|
||||||
params := waitForDeleteParams{
|
params := waitForDeleteParams{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
pods: []corev1.Pod{pod},
|
pods: []corev1.Pod{activePod},
|
||||||
interval: 1 * time.Second,
|
interval: 1 * time.Second,
|
||||||
timeout: time.Duration(math.MaxInt64),
|
timeout: time.Duration(math.MaxInt64),
|
||||||
usingEviction: true,
|
usingEviction: true,
|
||||||
|
|
|
@ -529,3 +529,110 @@ func TestFilterPods(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEvictDuringNamespaceTerminating(t *testing.T) {
|
||||||
|
testPodUID := types.UID("test-uid")
|
||||||
|
testPodName := "test-pod"
|
||||||
|
testNamespace := "default"
|
||||||
|
|
||||||
|
retryDelay := 5 * time.Millisecond
|
||||||
|
globalTimeout := 2 * retryDelay
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
refresh bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "Pod refreshed after NamespaceTerminating error",
|
||||||
|
refresh: true,
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Pod not refreshed after NamespaceTerminating error",
|
||||||
|
refresh: false,
|
||||||
|
err: fmt.Errorf("error when evicting pods/%q -n %q: global timeout reached: %v", testPodName, testNamespace, globalTimeout),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.description, func(t *testing.T) {
|
||||||
|
var retry bool
|
||||||
|
|
||||||
|
initialPod := &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: testPodName,
|
||||||
|
Namespace: testNamespace,
|
||||||
|
UID: testPodUID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// pod with DeletionTimestamp, indicating deletion in progress
|
||||||
|
deletedPod := &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: testPodName,
|
||||||
|
Namespace: testNamespace,
|
||||||
|
UID: testPodUID,
|
||||||
|
DeletionTimestamp: &metav1.Time{Time: time.Now()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
evictPods := []corev1.Pod{*initialPod}
|
||||||
|
|
||||||
|
k := fake.NewClientset(initialPod)
|
||||||
|
addEvictionSupport(t, k, "v1")
|
||||||
|
|
||||||
|
// mock eviction to return NamespaceTerminating error
|
||||||
|
k.PrependReactor("create", "pods", func(action ktest.Action) (bool, runtime.Object, error) {
|
||||||
|
if action.GetSubresource() != "eviction" {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := apierrors.NewForbidden(
|
||||||
|
schema.GroupResource{Resource: "pods"},
|
||||||
|
testPodName,
|
||||||
|
errors.New("namespace is terminating"),
|
||||||
|
)
|
||||||
|
|
||||||
|
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{
|
||||||
|
Type: corev1.NamespaceTerminatingCause,
|
||||||
|
})
|
||||||
|
|
||||||
|
return true, nil, err
|
||||||
|
})
|
||||||
|
|
||||||
|
k.PrependReactor("get", "pods", func(action ktest.Action) (bool, runtime.Object, error) {
|
||||||
|
if !test.refresh {
|
||||||
|
// for non-refresh test, always return the initial pod
|
||||||
|
return true, initialPod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if retry {
|
||||||
|
// second call, pod is deleted
|
||||||
|
return true, nil, apierrors.NewNotFound(schema.GroupResource{Resource: "pods"}, testPodName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first call, pod is being deleted
|
||||||
|
retry = true
|
||||||
|
|
||||||
|
return true, deletedPod, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
h := &Helper{
|
||||||
|
Client: k,
|
||||||
|
DisableEviction: false,
|
||||||
|
Out: os.Stdout,
|
||||||
|
ErrOut: os.Stderr,
|
||||||
|
Timeout: globalTimeout,
|
||||||
|
EvictErrorRetryDelay: retryDelay,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.DeleteOrEvictPods(evictPods)
|
||||||
|
if test.err == nil && err != nil {
|
||||||
|
t.Errorf("expected no error, got: %v", err)
|
||||||
|
} else if test.err != nil && (err == nil || err.Error() != test.err.Error()) {
|
||||||
|
t.Errorf("%s: unexpected eviction; actual %v; expected %v", test.description, err, test.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -319,16 +319,18 @@ func compGetResourceList(restClientGetter genericclioptions.RESTClientGetter, cm
|
||||||
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
|
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
|
||||||
o := apiresources.NewAPIResourceOptions(streams)
|
o := apiresources.NewAPIResourceOptions(streams)
|
||||||
|
|
||||||
o.Complete(restClientGetter, cmd, nil)
|
|
||||||
|
|
||||||
// Get the list of resources
|
// Get the list of resources
|
||||||
o.PrintFlags.OutputFormat = ptr.To("name")
|
o.PrintFlags.OutputFormat = ptr.To("name")
|
||||||
o.Cached = true
|
o.Cached = true
|
||||||
o.Verbs = []string{"get"}
|
o.Verbs = []string{"get"}
|
||||||
// TODO:Should set --request-timeout=5s
|
// TODO:Should set --request-timeout=5s
|
||||||
|
|
||||||
|
if err := o.Complete(restClientGetter, cmd, nil); err != nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore errors as the output may still be valid
|
// Ignore errors as the output may still be valid
|
||||||
o.RunAPIResources()
|
_ = o.RunAPIResources()
|
||||||
|
|
||||||
// Resources can be a comma-separated list. The last element is then
|
// Resources can be a comma-separated list. The last element is then
|
||||||
// the one we should complete. For example if toComplete=="pods,secre"
|
// the one we should complete. For example if toComplete=="pods,secre"
|
||||||
|
|
|
@ -492,6 +492,101 @@ func TestResourceAndPortCompletionFunc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceTypeAndNameCompletionFuncResourceList(t *testing.T) {
|
||||||
|
// Set up a fake discovery client with some API resources
|
||||||
|
dc := cmdtesting.NewFakeCachedDiscoveryClient()
|
||||||
|
dc.PreferredResources = []*metav1.APIResourceList{
|
||||||
|
{
|
||||||
|
GroupVersion: "v1",
|
||||||
|
APIResources: []metav1.APIResource{
|
||||||
|
{
|
||||||
|
Name: "pods",
|
||||||
|
Namespaced: true,
|
||||||
|
Kind: "Pod",
|
||||||
|
Verbs: []string{"get", "list"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "services",
|
||||||
|
Namespaced: true,
|
||||||
|
Kind: "Service",
|
||||||
|
Verbs: []string{"get", "list"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "secrets",
|
||||||
|
Namespaced: true,
|
||||||
|
Kind: "Secret",
|
||||||
|
Verbs: []string{"get", "list"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
GroupVersion: "apps/v1",
|
||||||
|
APIResources: []metav1.APIResource{
|
||||||
|
{
|
||||||
|
Name: "deployments",
|
||||||
|
Namespaced: true,
|
||||||
|
Kind: "Deployment",
|
||||||
|
Verbs: []string{"get", "list"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
toComplete string
|
||||||
|
expectedComps []string
|
||||||
|
expectedDirective cobra.ShellCompDirective
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "complete resources starting with 's'",
|
||||||
|
args: []string{},
|
||||||
|
toComplete: "s",
|
||||||
|
expectedComps: []string{"secrets", "services"},
|
||||||
|
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complete resources starting with 'p'",
|
||||||
|
args: []string{},
|
||||||
|
toComplete: "p",
|
||||||
|
expectedComps: []string{"pods"},
|
||||||
|
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complete resources starting with 'd'",
|
||||||
|
args: []string{},
|
||||||
|
toComplete: "d",
|
||||||
|
expectedComps: []string{"deployments.apps"},
|
||||||
|
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complete all resources with empty string",
|
||||||
|
args: []string{},
|
||||||
|
toComplete: "",
|
||||||
|
expectedComps: []string{"deployments.apps", "pods", "secrets", "services"},
|
||||||
|
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no matches",
|
||||||
|
args: []string{},
|
||||||
|
toComplete: "xyz",
|
||||||
|
expectedComps: []string{},
|
||||||
|
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tf, cmd := prepareCompletionTest()
|
||||||
|
tf.WithDiscoveryClient(dc)
|
||||||
|
compFunc := ResourceTypeAndNameCompletionFunc(tf)
|
||||||
|
comps, directive := compFunc(cmd, tc.args, tc.toComplete)
|
||||||
|
checkCompletion(t, comps, tc.expectedComps, directive, tc.expectedDirective)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setMockFactory(config api.Config) {
|
func setMockFactory(config api.Config) {
|
||||||
clientConfig := clientcmd.NewDefaultClientConfig(config, nil)
|
clientConfig := clientcmd.NewDefaultClientConfig(config, nil)
|
||||||
testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)
|
testFactory := cmdtesting.NewTestFactory().WithClientConfig(clientConfig)
|
||||||
|
|
|
@ -21,12 +21,28 @@ import (
|
||||||
|
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TerminalSize represents the width and height of a terminal.
|
||||||
|
// It is the same as staging/src/k8s.io/client-go/tools/remotecommand.TerminalSize.
|
||||||
|
// Copied to decouple the packages. Terminal-related package should not depend on API client and vice versa.
|
||||||
|
type TerminalSize struct {
|
||||||
|
Width uint16
|
||||||
|
Height uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// TerminalSizeQueue is capable of returning terminal resize events as they occur.
|
||||||
|
// It is the same as staging/src/k8s.io/client-go/tools/remotecommand.TerminalSizeQueue.
|
||||||
|
// Copied to decouple the packages. Terminal-related package should not depend on API client and vice versa.
|
||||||
|
type TerminalSizeQueue interface {
|
||||||
|
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
||||||
|
// monitoring has been stopped.
|
||||||
|
Next() *TerminalSize
|
||||||
|
}
|
||||||
|
|
||||||
// GetSize returns the current size of the user's terminal. If it isn't a terminal,
|
// GetSize returns the current size of the user's terminal. If it isn't a terminal,
|
||||||
// nil is returned.
|
// nil is returned.
|
||||||
func (t TTY) GetSize() *remotecommand.TerminalSize {
|
func (t TTY) GetSize() *TerminalSize {
|
||||||
outFd, isTerminal := term.GetFdInfo(t.Out)
|
outFd, isTerminal := term.GetFdInfo(t.Out)
|
||||||
if !isTerminal {
|
if !isTerminal {
|
||||||
return nil
|
return nil
|
||||||
|
@ -35,19 +51,19 @@ func (t TTY) GetSize() *remotecommand.TerminalSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSize returns the current size of the terminal associated with fd.
|
// GetSize returns the current size of the terminal associated with fd.
|
||||||
func GetSize(fd uintptr) *remotecommand.TerminalSize {
|
func GetSize(fd uintptr) *TerminalSize {
|
||||||
winsize, err := term.GetWinsize(fd)
|
winsize, err := term.GetWinsize(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
|
runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &remotecommand.TerminalSize{Width: winsize.Width, Height: winsize.Height}
|
return &TerminalSize{Width: winsize.Width, Height: winsize.Height}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
|
// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
|
||||||
// initialSizes, or nil if there's no TTY present.
|
// initialSizes, or nil if there's no TTY present.
|
||||||
func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecommand.TerminalSizeQueue {
|
func (t *TTY) MonitorSize(initialSizes ...*TerminalSize) TerminalSizeQueue {
|
||||||
outFd, isTerminal := term.GetFdInfo(t.Out)
|
outFd, isTerminal := term.GetFdInfo(t.Out)
|
||||||
if !isTerminal {
|
if !isTerminal {
|
||||||
return nil
|
return nil
|
||||||
|
@ -57,7 +73,7 @@ func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecom
|
||||||
t: *t,
|
t: *t,
|
||||||
// make it buffered so we can send the initial terminal sizes without blocking, prior to starting
|
// make it buffered so we can send the initial terminal sizes without blocking, prior to starting
|
||||||
// the streaming below
|
// the streaming below
|
||||||
resizeChan: make(chan remotecommand.TerminalSize, len(initialSizes)),
|
resizeChan: make(chan TerminalSize, len(initialSizes)),
|
||||||
stopResizing: make(chan struct{}),
|
stopResizing: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,16 +86,16 @@ func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecom
|
||||||
type sizeQueue struct {
|
type sizeQueue struct {
|
||||||
t TTY
|
t TTY
|
||||||
// resizeChan receives a Size each time the user's terminal is resized.
|
// resizeChan receives a Size each time the user's terminal is resized.
|
||||||
resizeChan chan remotecommand.TerminalSize
|
resizeChan chan TerminalSize
|
||||||
stopResizing chan struct{}
|
stopResizing chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure sizeQueue implements the resize.TerminalSizeQueue interface
|
// make sure sizeQueue implements the TerminalSizeQueue interface
|
||||||
var _ remotecommand.TerminalSizeQueue = &sizeQueue{}
|
var _ TerminalSizeQueue = &sizeQueue{}
|
||||||
|
|
||||||
// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
|
// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
|
||||||
// new event, it sends the current terminal size to resizeChan.
|
// new event, it sends the current terminal size to resizeChan.
|
||||||
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.TerminalSize) {
|
func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*TerminalSize) {
|
||||||
// send the initial sizes
|
// send the initial sizes
|
||||||
for i := range initialSizes {
|
for i := range initialSizes {
|
||||||
if initialSizes[i] != nil {
|
if initialSizes[i] != nil {
|
||||||
|
@ -87,7 +103,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.Te
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeEvents := make(chan remotecommand.TerminalSize, 1)
|
resizeEvents := make(chan TerminalSize, 1)
|
||||||
|
|
||||||
monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
|
monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
|
||||||
|
|
||||||
|
@ -118,7 +134,7 @@ func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.Te
|
||||||
|
|
||||||
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
// Next returns the new terminal size after the terminal has been resized. It returns nil when
|
||||||
// monitoring has been stopped.
|
// monitoring has been stopped.
|
||||||
func (s *sizeQueue) Next() *remotecommand.TerminalSize {
|
func (s *sizeQueue) Next() *TerminalSize {
|
||||||
size, ok := <-s.resizeChan
|
size, ok := <-s.resizeChan
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -25,13 +25,12 @@ import (
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the
|
// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the
|
||||||
// terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send
|
// terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send
|
||||||
// it to the resizeEvents channel. The goroutine stops when the stop channel is closed.
|
// it to the resizeEvents channel. The goroutine stops when the stop channel is closed.
|
||||||
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
|
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
|
||||||
go func() {
|
go func() {
|
||||||
defer runtime.HandleCrash()
|
defer runtime.HandleCrash()
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send
|
// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send
|
||||||
// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel
|
// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel
|
||||||
// is closed.
|
// is closed.
|
||||||
func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
|
func monitorResizeEvents(fd uintptr, resizeEvents chan<- TerminalSize, stop chan struct{}) {
|
||||||
go func() {
|
go func() {
|
||||||
defer runtime.HandleCrash()
|
defer runtime.HandleCrash()
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ import (
|
||||||
|
|
||||||
wordwrap "github.com/mitchellh/go-wordwrap"
|
wordwrap "github.com/mitchellh/go-wordwrap"
|
||||||
"github.com/moby/term"
|
"github.com/moby/term"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type wordWrapWriter struct {
|
type wordWrapWriter struct {
|
||||||
|
@ -70,7 +68,7 @@ func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTerminalLimitWidth(terminalSize *remotecommand.TerminalSize) uint {
|
func getTerminalLimitWidth(terminalSize *TerminalSize) uint {
|
||||||
var limit uint
|
var limit uint
|
||||||
switch {
|
switch {
|
||||||
case terminalSize.Width >= 120:
|
case terminalSize.Width >= 120:
|
||||||
|
|
Loading…
Reference in New Issue