...

Source file src/k8s.io/kubectl/pkg/cmd/set/set_image_test.go

Documentation: k8s.io/kubectl/pkg/cmd/set

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package set
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"net/http"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	appsv1beta1 "k8s.io/api/apps/v1beta1"
    30  	appsv1beta2 "k8s.io/api/apps/v1beta2"
    31  	batchv1 "k8s.io/api/batch/v1"
    32  	corev1 "k8s.io/api/core/v1"
    33  	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/runtime"
    36  	"k8s.io/apimachinery/pkg/runtime/schema"
    37  	"k8s.io/cli-runtime/pkg/genericclioptions"
    38  	"k8s.io/cli-runtime/pkg/genericiooptions"
    39  	"k8s.io/cli-runtime/pkg/resource"
    40  	restclient "k8s.io/client-go/rest"
    41  	"k8s.io/client-go/rest/fake"
    42  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    43  	"k8s.io/kubectl/pkg/scheme"
    44  )
    45  
    46  func TestImageLocal(t *testing.T) {
    47  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
    48  	defer tf.Cleanup()
    49  
    50  	tf.Client = &fake.RESTClient{
    51  		GroupVersion:         schema.GroupVersion{Version: ""},
    52  		NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
    53  		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
    54  			t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
    55  			return nil, nil
    56  		}),
    57  	}
    58  	tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
    59  
    60  	outputFormat := "name"
    61  
    62  	streams, _, buf, _ := genericiooptions.NewTestIOStreams()
    63  	cmd := NewCmdImage(tf, streams)
    64  	cmd.SetOut(buf)
    65  	cmd.SetErr(buf)
    66  	cmd.Flags().Set("output", outputFormat)
    67  	cmd.Flags().Set("local", "true")
    68  
    69  	opts := SetImageOptions{
    70  		PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
    71  		FilenameOptions: resource.FilenameOptions{
    72  			Filenames: []string{"../../../testdata/controller.yaml"}},
    73  		Local:     true,
    74  		IOStreams: streams,
    75  	}
    76  	err := opts.Complete(tf, cmd, []string{"cassandra=thingy"})
    77  	if err == nil {
    78  		err = opts.Validate()
    79  	}
    80  	if err == nil {
    81  		err = opts.Run()
    82  	}
    83  	if err != nil {
    84  		t.Fatalf("unexpected error: %v", err)
    85  	}
    86  	if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
    87  		t.Errorf("did not set image: %s", buf.String())
    88  	}
    89  }
    90  
    91  func TestSetImageValidation(t *testing.T) {
    92  	printFlags := genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme)
    93  
    94  	testCases := []struct {
    95  		name         string
    96  		imageOptions *SetImageOptions
    97  		expectErr    string
    98  	}{
    99  		{
   100  			name:         "test resource < 1 and filenames empty",
   101  			imageOptions: &SetImageOptions{PrintFlags: printFlags},
   102  			expectErr:    "[one or more resources must be specified as <resource> <name> or <resource>/<name>, at least one image update is required]",
   103  		},
   104  		{
   105  			name: "test containerImages < 1",
   106  			imageOptions: &SetImageOptions{
   107  				PrintFlags: printFlags,
   108  				Resources:  []string{"a", "b", "c"},
   109  
   110  				FilenameOptions: resource.FilenameOptions{
   111  					Filenames: []string{"testFile"},
   112  				},
   113  			},
   114  			expectErr: "at least one image update is required",
   115  		},
   116  		{
   117  			name: "test containerImages > 1 and all containers are already specified by *",
   118  			imageOptions: &SetImageOptions{
   119  				PrintFlags: printFlags,
   120  				Resources:  []string{"a", "b", "c"},
   121  				FilenameOptions: resource.FilenameOptions{
   122  					Filenames: []string{"testFile"},
   123  				},
   124  				ContainerImages: map[string]string{
   125  					"test": "test",
   126  					"*":    "test",
   127  				},
   128  			},
   129  			expectErr: "all containers are already specified by *, but saw more than one container_name=container_image pairs",
   130  		},
   131  		{
   132  			name: "success case",
   133  			imageOptions: &SetImageOptions{
   134  				PrintFlags: printFlags,
   135  				Resources:  []string{"a", "b", "c"},
   136  				FilenameOptions: resource.FilenameOptions{
   137  					Filenames: []string{"testFile"},
   138  				},
   139  				ContainerImages: map[string]string{
   140  					"test": "test",
   141  				},
   142  			},
   143  			expectErr: "",
   144  		},
   145  	}
   146  	for _, testCase := range testCases {
   147  		err := testCase.imageOptions.Validate()
   148  		if err != nil {
   149  			if err.Error() != testCase.expectErr {
   150  				t.Errorf("[%s]:expect err:%s got err:%s", testCase.name, testCase.expectErr, err.Error())
   151  			}
   152  		}
   153  		if err == nil && (testCase.expectErr != "") {
   154  			t.Errorf("[%s]:expect err:%s got err:%v", testCase.name, testCase.expectErr, err)
   155  		}
   156  	}
   157  }
   158  
   159  func TestSetMultiResourcesImageLocal(t *testing.T) {
   160  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
   161  	defer tf.Cleanup()
   162  
   163  	tf.Client = &fake.RESTClient{
   164  		GroupVersion:         schema.GroupVersion{Version: ""},
   165  		NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
   166  		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   167  			t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
   168  			return nil, nil
   169  		}),
   170  	}
   171  	tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
   172  
   173  	outputFormat := "name"
   174  
   175  	streams, _, buf, _ := genericiooptions.NewTestIOStreams()
   176  	cmd := NewCmdImage(tf, streams)
   177  	cmd.SetOut(buf)
   178  	cmd.SetErr(buf)
   179  	cmd.Flags().Set("output", outputFormat)
   180  	cmd.Flags().Set("local", "true")
   181  
   182  	opts := SetImageOptions{
   183  		PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
   184  		FilenameOptions: resource.FilenameOptions{
   185  			Filenames: []string{"../../../testdata/set/multi-resource-yaml.yaml"}},
   186  		Local:     true,
   187  		IOStreams: streams,
   188  	}
   189  	err := opts.Complete(tf, cmd, []string{"*=thingy"})
   190  	if err == nil {
   191  		err = opts.Validate()
   192  	}
   193  	if err == nil {
   194  		err = opts.Run()
   195  	}
   196  	if err != nil {
   197  		t.Fatalf("unexpected error: %v", err)
   198  	}
   199  	expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
   200  	if buf.String() != expectedOut {
   201  		t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
   202  	}
   203  }
   204  
   205  func TestSetImageRemote(t *testing.T) {
   206  	inputs := []struct {
   207  		name         string
   208  		object       runtime.Object
   209  		groupVersion schema.GroupVersion
   210  		path         string
   211  		args         []string
   212  	}{
   213  		{
   214  			name: "set image extensionsv1beta1 ReplicaSet",
   215  			object: &extensionsv1beta1.ReplicaSet{
   216  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   217  				Spec: extensionsv1beta1.ReplicaSetSpec{
   218  					Template: corev1.PodTemplateSpec{
   219  						Spec: corev1.PodSpec{
   220  							Containers: []corev1.Container{
   221  								{
   222  									Name:  "nginx",
   223  									Image: "nginx",
   224  								},
   225  							},
   226  							InitContainers: []corev1.Container{
   227  								{
   228  									Name:  "busybox",
   229  									Image: "busybox",
   230  								},
   231  							},
   232  						},
   233  					},
   234  				},
   235  			},
   236  			groupVersion: extensionsv1beta1.SchemeGroupVersion,
   237  			path:         "/namespaces/test/replicasets/nginx",
   238  			args:         []string{"replicaset", "nginx", "*=thingy"},
   239  		},
   240  		{
   241  			name: "set image appsv1beta2 ReplicaSet",
   242  			object: &appsv1beta2.ReplicaSet{
   243  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   244  				Spec: appsv1beta2.ReplicaSetSpec{
   245  					Template: corev1.PodTemplateSpec{
   246  						Spec: corev1.PodSpec{
   247  							Containers: []corev1.Container{
   248  								{
   249  									Name:  "nginx",
   250  									Image: "nginx",
   251  								},
   252  							},
   253  							InitContainers: []corev1.Container{
   254  								{
   255  									Name:  "busybox",
   256  									Image: "busybox",
   257  								},
   258  							},
   259  						},
   260  					},
   261  				},
   262  			},
   263  			groupVersion: appsv1beta2.SchemeGroupVersion,
   264  			path:         "/namespaces/test/replicasets/nginx",
   265  			args:         []string{"replicaset", "nginx", "*=thingy"},
   266  		},
   267  		{
   268  			name: "set image appsv1 ReplicaSet",
   269  			object: &appsv1.ReplicaSet{
   270  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   271  				Spec: appsv1.ReplicaSetSpec{
   272  					Template: corev1.PodTemplateSpec{
   273  						Spec: corev1.PodSpec{
   274  							Containers: []corev1.Container{
   275  								{
   276  									Name:  "nginx",
   277  									Image: "nginx",
   278  								},
   279  							},
   280  							InitContainers: []corev1.Container{
   281  								{
   282  									Name:  "busybox",
   283  									Image: "busybox",
   284  								},
   285  							},
   286  						},
   287  					},
   288  				},
   289  			},
   290  			groupVersion: appsv1.SchemeGroupVersion,
   291  			path:         "/namespaces/test/replicasets/nginx",
   292  			args:         []string{"replicaset", "nginx", "*=thingy"},
   293  		},
   294  		{
   295  			name: "set image extensionsv1beta1 DaemonSet",
   296  			object: &extensionsv1beta1.DaemonSet{
   297  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   298  				Spec: extensionsv1beta1.DaemonSetSpec{
   299  					Template: corev1.PodTemplateSpec{
   300  						Spec: corev1.PodSpec{
   301  							Containers: []corev1.Container{
   302  								{
   303  									Name:  "nginx",
   304  									Image: "nginx",
   305  								},
   306  							},
   307  							InitContainers: []corev1.Container{
   308  								{
   309  									Name:  "busybox",
   310  									Image: "busybox",
   311  								},
   312  							},
   313  						},
   314  					},
   315  				},
   316  			},
   317  			groupVersion: extensionsv1beta1.SchemeGroupVersion,
   318  			path:         "/namespaces/test/daemonsets/nginx",
   319  			args:         []string{"daemonset", "nginx", "*=thingy"},
   320  		},
   321  		{
   322  			name: "set image appsv1beta2 DaemonSet",
   323  			object: &appsv1beta2.DaemonSet{
   324  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   325  				Spec: appsv1beta2.DaemonSetSpec{
   326  					Template: corev1.PodTemplateSpec{
   327  						Spec: corev1.PodSpec{
   328  							Containers: []corev1.Container{
   329  								{
   330  									Name:  "nginx",
   331  									Image: "nginx",
   332  								},
   333  							},
   334  							InitContainers: []corev1.Container{
   335  								{
   336  									Name:  "busybox",
   337  									Image: "busybox",
   338  								},
   339  							},
   340  						},
   341  					},
   342  				},
   343  			},
   344  			groupVersion: appsv1beta2.SchemeGroupVersion,
   345  			path:         "/namespaces/test/daemonsets/nginx",
   346  			args:         []string{"daemonset", "nginx", "*=thingy"},
   347  		},
   348  		{
   349  			name: "set image appsv1 DaemonSet",
   350  			object: &appsv1.DaemonSet{
   351  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   352  				Spec: appsv1.DaemonSetSpec{
   353  					Template: corev1.PodTemplateSpec{
   354  						Spec: corev1.PodSpec{
   355  							Containers: []corev1.Container{
   356  								{
   357  									Name:  "nginx",
   358  									Image: "nginx",
   359  								},
   360  							},
   361  							InitContainers: []corev1.Container{
   362  								{
   363  									Name:  "busybox",
   364  									Image: "busybox",
   365  								},
   366  							},
   367  						},
   368  					},
   369  				},
   370  			},
   371  			groupVersion: appsv1.SchemeGroupVersion,
   372  			path:         "/namespaces/test/daemonsets/nginx",
   373  			args:         []string{"daemonset", "nginx", "*=thingy"},
   374  		},
   375  		{
   376  			name: "set image extensionsv1beta1 Deployment",
   377  			object: &extensionsv1beta1.Deployment{
   378  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   379  				Spec: extensionsv1beta1.DeploymentSpec{
   380  					Template: corev1.PodTemplateSpec{
   381  						Spec: corev1.PodSpec{
   382  							Containers: []corev1.Container{
   383  								{
   384  									Name:  "nginx",
   385  									Image: "nginx",
   386  								},
   387  							},
   388  							InitContainers: []corev1.Container{
   389  								{
   390  									Name:  "busybox",
   391  									Image: "busybox",
   392  								},
   393  							},
   394  						},
   395  					},
   396  				},
   397  			},
   398  			groupVersion: extensionsv1beta1.SchemeGroupVersion,
   399  			path:         "/namespaces/test/deployments/nginx",
   400  			args:         []string{"deployment", "nginx", "*=thingy"},
   401  		},
   402  		{
   403  			name: "set image appsv1beta1 Deployment",
   404  			object: &appsv1beta1.Deployment{
   405  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   406  				Spec: appsv1beta1.DeploymentSpec{
   407  					Template: corev1.PodTemplateSpec{
   408  						Spec: corev1.PodSpec{
   409  							Containers: []corev1.Container{
   410  								{
   411  									Name:  "nginx",
   412  									Image: "nginx",
   413  								},
   414  							},
   415  							InitContainers: []corev1.Container{
   416  								{
   417  									Name:  "busybox",
   418  									Image: "busybox",
   419  								},
   420  							},
   421  						},
   422  					},
   423  				},
   424  			},
   425  			groupVersion: appsv1beta1.SchemeGroupVersion,
   426  			path:         "/namespaces/test/deployments/nginx",
   427  			args:         []string{"deployment", "nginx", "*=thingy"},
   428  		},
   429  		{
   430  			name: "set image appsv1beta2 Deployment",
   431  			object: &appsv1beta2.Deployment{
   432  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   433  				Spec: appsv1beta2.DeploymentSpec{
   434  					Template: corev1.PodTemplateSpec{
   435  						Spec: corev1.PodSpec{
   436  							Containers: []corev1.Container{
   437  								{
   438  									Name:  "nginx",
   439  									Image: "nginx",
   440  								},
   441  							},
   442  							InitContainers: []corev1.Container{
   443  								{
   444  									Name:  "busybox",
   445  									Image: "busybox",
   446  								},
   447  							},
   448  						},
   449  					},
   450  				},
   451  			},
   452  			groupVersion: appsv1beta2.SchemeGroupVersion,
   453  			path:         "/namespaces/test/deployments/nginx",
   454  			args:         []string{"deployment", "nginx", "*=thingy"},
   455  		},
   456  		{
   457  			name: "set image appsv1 Deployment",
   458  			object: &appsv1.Deployment{
   459  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   460  				Spec: appsv1.DeploymentSpec{
   461  					Template: corev1.PodTemplateSpec{
   462  						Spec: corev1.PodSpec{
   463  							Containers: []corev1.Container{
   464  								{
   465  									Name:  "nginx",
   466  									Image: "nginx",
   467  								},
   468  							},
   469  							InitContainers: []corev1.Container{
   470  								{
   471  									Name:  "busybox",
   472  									Image: "busybox",
   473  								},
   474  							},
   475  						},
   476  					},
   477  				},
   478  			},
   479  			groupVersion: appsv1.SchemeGroupVersion,
   480  			path:         "/namespaces/test/deployments/nginx",
   481  			args:         []string{"deployment", "nginx", "*=thingy"},
   482  		},
   483  		{
   484  			name: "set image appsv1beta1 StatefulSet",
   485  			object: &appsv1beta1.StatefulSet{
   486  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   487  				Spec: appsv1beta1.StatefulSetSpec{
   488  					Template: corev1.PodTemplateSpec{
   489  						Spec: corev1.PodSpec{
   490  							Containers: []corev1.Container{
   491  								{
   492  									Name:  "nginx",
   493  									Image: "nginx",
   494  								},
   495  							},
   496  							InitContainers: []corev1.Container{
   497  								{
   498  									Name:  "busybox",
   499  									Image: "busybox",
   500  								},
   501  							},
   502  						},
   503  					},
   504  				},
   505  			},
   506  			groupVersion: appsv1beta1.SchemeGroupVersion,
   507  			path:         "/namespaces/test/statefulsets/nginx",
   508  			args:         []string{"statefulset", "nginx", "*=thingy"},
   509  		},
   510  		{
   511  			name: "set image appsv1beta2 StatefulSet",
   512  			object: &appsv1beta2.StatefulSet{
   513  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   514  				Spec: appsv1beta2.StatefulSetSpec{
   515  					Template: corev1.PodTemplateSpec{
   516  						Spec: corev1.PodSpec{
   517  							Containers: []corev1.Container{
   518  								{
   519  									Name:  "nginx",
   520  									Image: "nginx",
   521  								},
   522  							},
   523  							InitContainers: []corev1.Container{
   524  								{
   525  									Name:  "busybox",
   526  									Image: "busybox",
   527  								},
   528  							},
   529  						},
   530  					},
   531  				},
   532  			},
   533  			groupVersion: appsv1beta2.SchemeGroupVersion,
   534  			path:         "/namespaces/test/statefulsets/nginx",
   535  			args:         []string{"statefulset", "nginx", "*=thingy"},
   536  		},
   537  		{
   538  			name: "set image appsv1 StatefulSet",
   539  			object: &appsv1.StatefulSet{
   540  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   541  				Spec: appsv1.StatefulSetSpec{
   542  					Template: corev1.PodTemplateSpec{
   543  						Spec: corev1.PodSpec{
   544  							Containers: []corev1.Container{
   545  								{
   546  									Name:  "nginx",
   547  									Image: "nginx",
   548  								},
   549  							},
   550  							InitContainers: []corev1.Container{
   551  								{
   552  									Name:  "busybox",
   553  									Image: "busybox",
   554  								},
   555  							},
   556  						},
   557  					},
   558  				},
   559  			},
   560  			groupVersion: appsv1.SchemeGroupVersion,
   561  			path:         "/namespaces/test/statefulsets/nginx",
   562  			args:         []string{"statefulset", "nginx", "*=thingy"},
   563  		},
   564  		{
   565  			name: "set image batchv1 CronJob",
   566  			object: &batchv1.CronJob{
   567  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   568  				Spec: batchv1.CronJobSpec{
   569  					JobTemplate: batchv1.JobTemplateSpec{
   570  						Spec: batchv1.JobSpec{
   571  							Template: corev1.PodTemplateSpec{
   572  								Spec: corev1.PodSpec{
   573  									Containers: []corev1.Container{
   574  										{
   575  											Name:  "nginx",
   576  											Image: "nginx",
   577  										},
   578  									},
   579  									InitContainers: []corev1.Container{
   580  										{
   581  											Name:  "busybox",
   582  											Image: "busybox",
   583  										},
   584  									},
   585  								},
   586  							},
   587  						},
   588  					},
   589  				},
   590  			},
   591  			groupVersion: batchv1.SchemeGroupVersion,
   592  			path:         "/namespaces/test/cronjobs/nginx",
   593  			args:         []string{"cronjob", "nginx", "*=thingy"},
   594  		},
   595  		{
   596  			name: "set image corev1.ReplicationController",
   597  			object: &corev1.ReplicationController{
   598  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   599  				Spec: corev1.ReplicationControllerSpec{
   600  					Template: &corev1.PodTemplateSpec{
   601  						Spec: corev1.PodSpec{
   602  							Containers: []corev1.Container{
   603  								{
   604  									Name:  "nginx",
   605  									Image: "nginx",
   606  								},
   607  							},
   608  							InitContainers: []corev1.Container{
   609  								{
   610  									Name:  "busybox",
   611  									Image: "busybox",
   612  								},
   613  							},
   614  						},
   615  					},
   616  				},
   617  			},
   618  			groupVersion: corev1.SchemeGroupVersion,
   619  			path:         "/namespaces/test/replicationcontrollers/nginx",
   620  			args:         []string{"replicationcontroller", "nginx", "*=thingy"},
   621  		},
   622  	}
   623  	for _, input := range inputs {
   624  		t.Run(input.name, func(t *testing.T) {
   625  			tf := cmdtesting.NewTestFactory().WithNamespace("test")
   626  			defer tf.Cleanup()
   627  
   628  			tf.Client = &fake.RESTClient{
   629  				GroupVersion:         input.groupVersion,
   630  				NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
   631  				Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   632  					switch p, m := req.URL.Path, req.Method; {
   633  					case p == input.path && m == http.MethodGet:
   634  						return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
   635  					case p == input.path && m == http.MethodPatch:
   636  						stream, err := req.GetBody()
   637  						if err != nil {
   638  							return nil, err
   639  						}
   640  						bytes, err := io.ReadAll(stream)
   641  						if err != nil {
   642  							return nil, err
   643  						}
   644  						assert.Contains(t, string(bytes), `"image":`+`"`+"thingy"+`"`, fmt.Sprintf("image not updated for %#v", input.object))
   645  						return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
   646  					default:
   647  						t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
   648  						return nil, fmt.Errorf("unexpected request")
   649  					}
   650  				}),
   651  			}
   652  
   653  			outputFormat := "yaml"
   654  
   655  			streams := genericiooptions.NewTestIOStreamsDiscard()
   656  			cmd := NewCmdImage(tf, streams)
   657  			cmd.Flags().Set("output", outputFormat)
   658  			opts := SetImageOptions{
   659  				PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
   660  
   661  				Local:     false,
   662  				IOStreams: streams,
   663  			}
   664  			err := opts.Complete(tf, cmd, input.args)
   665  			assert.NoError(t, err)
   666  			err = opts.Run()
   667  			assert.NoError(t, err)
   668  		})
   669  	}
   670  }
   671  
   672  func TestSetImageRemoteWithSpecificContainers(t *testing.T) {
   673  	inputs := []struct {
   674  		name         string
   675  		object       runtime.Object
   676  		groupVersion schema.GroupVersion
   677  		path         string
   678  		args         []string
   679  	}{
   680  		{
   681  			name: "set container image only",
   682  			object: &extensionsv1beta1.ReplicaSet{
   683  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   684  				Spec: extensionsv1beta1.ReplicaSetSpec{
   685  					Template: corev1.PodTemplateSpec{
   686  						Spec: corev1.PodSpec{
   687  							Containers: []corev1.Container{
   688  								{
   689  									Name:  "nginx",
   690  									Image: "nginx",
   691  								},
   692  							},
   693  							InitContainers: []corev1.Container{
   694  								{
   695  									Name:  "busybox",
   696  									Image: "busybox",
   697  								},
   698  							},
   699  						},
   700  					},
   701  				},
   702  			},
   703  			groupVersion: extensionsv1beta1.SchemeGroupVersion,
   704  			path:         "/namespaces/test/replicasets/nginx",
   705  			args:         []string{"replicaset", "nginx", "nginx=thingy"},
   706  		},
   707  		{
   708  			name: "set initContainer image only",
   709  			object: &appsv1beta2.ReplicaSet{
   710  				ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
   711  				Spec: appsv1beta2.ReplicaSetSpec{
   712  					Template: corev1.PodTemplateSpec{
   713  						Spec: corev1.PodSpec{
   714  							Containers: []corev1.Container{
   715  								{
   716  									Name:  "busybox",
   717  									Image: "busybox",
   718  								},
   719  							},
   720  							InitContainers: []corev1.Container{
   721  								{
   722  									Name:  "nginx",
   723  									Image: "nginx",
   724  								},
   725  							},
   726  						},
   727  					},
   728  				},
   729  			},
   730  			groupVersion: appsv1beta2.SchemeGroupVersion,
   731  			path:         "/namespaces/test/replicasets/nginx",
   732  			args:         []string{"replicaset", "nginx", "nginx=thingy"},
   733  		},
   734  	}
   735  	for _, input := range inputs {
   736  		t.Run(input.name, func(t *testing.T) {
   737  			tf := cmdtesting.NewTestFactory().WithNamespace("test")
   738  			defer tf.Cleanup()
   739  
   740  			tf.Client = &fake.RESTClient{
   741  				GroupVersion:         input.groupVersion,
   742  				NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
   743  				Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   744  					switch p, m := req.URL.Path, req.Method; {
   745  					case p == input.path && m == http.MethodGet:
   746  						return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
   747  					case p == input.path && m == http.MethodPatch:
   748  						stream, err := req.GetBody()
   749  						if err != nil {
   750  							return nil, err
   751  						}
   752  						bytes, err := io.ReadAll(stream)
   753  						if err != nil {
   754  							return nil, err
   755  						}
   756  						assert.Contains(t, string(bytes), `"image":"`+"thingy"+`","name":`+`"nginx"`, fmt.Sprintf("image not updated for %#v", input.object))
   757  						assert.NotContains(t, string(bytes), `"image":"`+"thingy"+`","name":`+`"busybox"`, fmt.Sprintf("image updated for %#v", input.object))
   758  						return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
   759  					default:
   760  						t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
   761  						return nil, fmt.Errorf("unexpected request")
   762  					}
   763  				}),
   764  			}
   765  
   766  			outputFormat := "yaml"
   767  
   768  			streams := genericiooptions.NewTestIOStreamsDiscard()
   769  			cmd := NewCmdImage(tf, streams)
   770  			cmd.Flags().Set("output", outputFormat)
   771  			opts := SetImageOptions{
   772  				PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
   773  
   774  				Local:     false,
   775  				IOStreams: streams,
   776  			}
   777  			err := opts.Complete(tf, cmd, input.args)
   778  			assert.NoError(t, err)
   779  			err = opts.Run()
   780  			assert.NoError(t, err)
   781  		})
   782  	}
   783  }
   784  
   785  func TestSetImageResolver(t *testing.T) {
   786  	f := func(in string) (string, error) {
   787  		return "custom", nil
   788  	}
   789  
   790  	ImageResolver = f
   791  
   792  	out, err := ImageResolver("my-image")
   793  	if err != nil {
   794  		t.Errorf("unexpected error from ImageResolver: %v", err)
   795  	} else if out != "custom" {
   796  		t.Errorf("expected: %s, found: %s", "custom", out)
   797  	}
   798  }
   799  

View as plain text