Merge pull request #101669 from carlory/fix-kubectl-1042

fix kubectl set env or resources not working for initcontainers

Kubernetes-commit: 8634bc61c635717dec93128f8908ffd20774e66f
This commit is contained in:
Kubernetes Publisher 2021-05-12 23:44:26 -07:00
commit d005bb9a51
7 changed files with 227 additions and 15 deletions

6
Godeps/Godeps.json generated
View file

@ -1040,11 +1040,11 @@
},
{
"ImportPath": "k8s.io/api",
"Rev": "020341df4dc8"
"Rev": "929601b97a81"
},
{
"ImportPath": "k8s.io/apimachinery",
"Rev": "96c076bf1d97"
"Rev": "4c2cee4b928c"
},
{
"ImportPath": "k8s.io/cli-runtime",
@ -1060,7 +1060,7 @@
},
{
"ImportPath": "k8s.io/component-base",
"Rev": "44faecbf614e"
"Rev": "18af7a2c44df"
},
{
"ImportPath": "k8s.io/component-helpers",

12
go.mod
View file

@ -32,11 +32,11 @@ require (
github.com/stretchr/testify v1.6.1
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.0.0-20210518101613-020341df4dc8
k8s.io/apimachinery v0.0.0-20210518100457-96c076bf1d97
k8s.io/api v0.0.0-20210518101620-929601b97a81
k8s.io/apimachinery v0.0.0-20210518100458-4c2cee4b928c
k8s.io/cli-runtime v0.0.0-20210518124924-2b12152cfa0d
k8s.io/client-go v0.0.0-20210518102931-3cca9d72c140
k8s.io/component-base v0.0.0-20210518111232-44faecbf614e
k8s.io/component-base v0.0.0-20210518111236-18af7a2c44df
k8s.io/component-helpers v0.0.0-20210518112016-d2cb76f2050a
k8s.io/klog/v2 v2.8.0
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
@ -48,12 +48,12 @@ require (
)
replace (
k8s.io/api => k8s.io/api v0.0.0-20210518101613-020341df4dc8
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210518100457-96c076bf1d97
k8s.io/api => k8s.io/api v0.0.0-20210518101620-929601b97a81
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20210518100458-4c2cee4b928c
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20210518124924-2b12152cfa0d
k8s.io/client-go => k8s.io/client-go v0.0.0-20210518102931-3cca9d72c140
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20210518095634-f57bba428cbd
k8s.io/component-base => k8s.io/component-base v0.0.0-20210518111232-44faecbf614e
k8s.io/component-base => k8s.io/component-base v0.0.0-20210518111236-18af7a2c44df
k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20210518112016-d2cb76f2050a
k8s.io/metrics => k8s.io/metrics v0.0.0-20210518124134-efe7adfc18ed
)

12
go.sum
View file

@ -731,17 +731,17 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.0.0-20210518101613-020341df4dc8 h1:ZRnEhwbQkikrh5x6TweXojo/X728NYotNn0j+tvQMfM=
k8s.io/api v0.0.0-20210518101613-020341df4dc8/go.mod h1:mfjSaVjQka7B+LlvX2q/KG3mXhs8HgdQT3ipatqYfU0=
k8s.io/apimachinery v0.0.0-20210518100457-96c076bf1d97 h1:hKyy5Pr8MoGjcqM5AYHQjpizciRmC8dz4N3XG4LZwuQ=
k8s.io/apimachinery v0.0.0-20210518100457-96c076bf1d97/go.mod h1:fBRSkoylGO2QUTae8Wb2wac6pZ83/r+tL6HFSXGbzfs=
k8s.io/api v0.0.0-20210518101620-929601b97a81 h1:HNRijF8MROYeNc8pUar2jkVz5vjJYD6ITNzWvdETjD4=
k8s.io/api v0.0.0-20210518101620-929601b97a81/go.mod h1:y+gvByfVKNWO6mErcyYTqghJvmW73GJQPxjlYKfOuio=
k8s.io/apimachinery v0.0.0-20210518100458-4c2cee4b928c h1:4cylLlCjbU4MC+jV8O68gmICP2kk8agE4tceRMAljVs=
k8s.io/apimachinery v0.0.0-20210518100458-4c2cee4b928c/go.mod h1:fBRSkoylGO2QUTae8Wb2wac6pZ83/r+tL6HFSXGbzfs=
k8s.io/cli-runtime v0.0.0-20210518124924-2b12152cfa0d h1:RFVr7FWdCdM4VFNKGXOLiPTJ/J1JVdBbTKY+ZPMIWXA=
k8s.io/cli-runtime v0.0.0-20210518124924-2b12152cfa0d/go.mod h1:YJCKepg/QD72rmBybA7hshzZfcAtjpbcMerf6dyv29g=
k8s.io/client-go v0.0.0-20210518102931-3cca9d72c140 h1:GY3mSlGtHMBhojVkVlvYMRGBFJPznw4duMxd8fMQvvg=
k8s.io/client-go v0.0.0-20210518102931-3cca9d72c140/go.mod h1:sGoh5eJ2yx5LuKX3C3Wj5zkHlcfHoueMPwbNP2qyRD8=
k8s.io/code-generator v0.0.0-20210518095634-f57bba428cbd/go.mod h1:tHNeGA58jE3nJvZLDN0c/5k7P3NlYdjbWuJYK9QiU3s=
k8s.io/component-base v0.0.0-20210518111232-44faecbf614e h1:+LvsEqL+o2/s3SNrTdZm22miglZOm3TPof8oO5pa2F8=
k8s.io/component-base v0.0.0-20210518111232-44faecbf614e/go.mod h1:VYU6k+gXEg7lXm+XCubCKvSlHHI9l+JHRJ0Hw4nrjug=
k8s.io/component-base v0.0.0-20210518111236-18af7a2c44df h1:Qz7sUwbLJpc/VUuBtxZb3seTfKV1hDTx1yAVPI7jnJI=
k8s.io/component-base v0.0.0-20210518111236-18af7a2c44df/go.mod h1:hPm8b9lzwMCIUcu2WyNo0MxrfZcmt+tMkH0FuhUs1Zo=
k8s.io/component-helpers v0.0.0-20210518112016-d2cb76f2050a h1:8/j0qwWx0p2tkFBK+URPu6ANIqp1MrZumZvlvfM7FwA=
k8s.io/component-helpers v0.0.0-20210518112016-d2cb76f2050a/go.mod h1:h0T3Vxhshgu1I5OuwwqkDdxOM7E3qsIDOA+hnvgxI7M=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=

View file

@ -365,7 +365,9 @@ func (o *EnvOptions) RunEnv() error {
patches := CalculatePatches(infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {
_, err := o.updatePodSpecForObject(obj, func(spec *v1.PodSpec) error {
resolutionErrorsEncountered := false
initContainers, _ := selectContainers(spec.InitContainers, o.ContainerSelector)
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
containers = append(containers, initContainers...)
objName, err := meta.NewAccessor().Name(obj)
if err != nil {
return err

View file

@ -666,3 +666,102 @@ func TestSetEnvFromResource(t *testing.T) {
})
}
}
func TestSetEnvRemoteWithSpecificContainers(t *testing.T) {
inputs := []struct {
name string
args []string
selector string
expectedContainers int
}{
{
name: "all containers",
args: []string{"deployments", "redis", "env=prod"},
selector: "*",
expectedContainers: 2,
},
{
name: "use wildcards to select some containers",
args: []string{"deployments", "redis", "env=prod"},
selector: "red*",
expectedContainers: 1,
},
{
name: "single container",
args: []string{"deployments", "redis", "env=prod"},
selector: "redis",
expectedContainers: 1,
},
}
for _, input := range inputs {
mockDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "redis",
Namespace: "test",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init",
Image: "redis",
},
},
Containers: []corev1.Container{
{
Name: "redis",
Image: "redis",
},
},
},
},
},
}
t.Run(input.name, func(t *testing.T) {
tf := cmdtesting.NewTestFactory().WithNamespace("test")
defer tf.Cleanup()
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/deployments/redis" && m == http.MethodGet:
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
case p == "/namespaces/test/deployments/redis" && m == http.MethodPatch:
stream, err := req.GetBody()
if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(stream)
if err != nil {
return nil, err
}
updated := strings.Count(string(bytes), `"value":`+`"`+"prod"+`"`)
if updated != input.expectedContainers {
t.Errorf("expected %d containers to be selected but got %d \n", input.expectedContainers, updated)
}
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
default:
t.Errorf("%s: unexpected request: %#v\n%#v", input.name, req.URL, req)
return nil, nil
}
}),
}
streams := genericclioptions.NewTestIOStreamsDiscard()
opts := &EnvOptions{
PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput("yaml").WithTypeSetter(scheme.Scheme),
ContainerSelector: input.selector,
Overwrite: true,
IOStreams: streams,
}
err := opts.Complete(tf, NewCmdEnv(tf, streams), input.args)
assert.NoError(t, err)
err = opts.RunEnv()
assert.NoError(t, err)
})
}
}

View file

@ -232,7 +232,9 @@ func (o *SetResourcesOptions) Run() error {
patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {
transformed := false
_, err := o.UpdatePodSpecForObject(obj, func(spec *v1.PodSpec) error {
initContainers, _ := selectContainers(spec.InitContainers, o.ContainerSelector)
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
containers = append(containers, initContainers...)
if len(containers) != 0 {
for i := range containers {
if len(o.Limits) != 0 && len(containers[i].Resources.Limits) == 0 {

View file

@ -516,3 +516,112 @@ func TestSetResourcesRemote(t *testing.T) {
})
}
}
func TestSetResourcesRemoteWithSpecificContainers(t *testing.T) {
inputs := []struct {
name string
selector string
args []string
expectedContainers int
}{
{
name: "all containers",
args: []string{"deployments", "redis"},
selector: "*",
expectedContainers: 2,
},
{
name: "use wildcards to select some containers",
args: []string{"deployments", "redis"},
selector: "red*",
expectedContainers: 1,
},
{
name: "single container",
args: []string{"deployments", "redis"},
selector: "redis",
expectedContainers: 1,
},
}
for _, input := range inputs {
mockDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "redis",
Namespace: "test",
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init",
Image: "redis",
},
},
Containers: []corev1.Container{
{
Name: "redis",
Image: "redis",
},
},
},
},
},
}
t.Run(input.name, func(t *testing.T) {
tf := cmdtesting.NewTestFactory().WithNamespace("test")
defer tf.Cleanup()
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/namespaces/test/deployments/redis" && m == http.MethodGet:
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
case p == "/namespaces/test/deployments/redis" && m == http.MethodPatch:
stream, err := req.GetBody()
if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(stream)
if err != nil {
return nil, err
}
updated := strings.Count(string(bytes), "200m")
if updated != input.expectedContainers {
t.Errorf("expected %d containers to be selected but got %d \n", input.expectedContainers, updated)
}
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
default:
t.Errorf("%s: unexpected request: %#v\n%#v", input.name, req.URL, req)
return nil, nil
}
}),
}
outputFormat := "yaml"
streams := genericclioptions.NewTestIOStreamsDiscard()
cmd := NewCmdResources(tf, streams)
cmd.Flags().Set("output", outputFormat)
opts := SetResourcesOptions{
PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
Limits: "cpu=200m,memory=512Mi",
ContainerSelector: input.selector,
IOStreams: streams,
}
err := opts.Complete(tf, cmd, input.args)
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.Run()
}
assert.NoError(t, err)
})
}
}