1
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 appsv1 "k8s.io/api/apps/v1"
28 appsv1beta1 "k8s.io/api/apps/v1beta1"
29 appsv1beta2 "k8s.io/api/apps/v1beta2"
30 batchv1 "k8s.io/api/batch/v1"
31 corev1 "k8s.io/api/core/v1"
32 extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34 "k8s.io/apimachinery/pkg/runtime"
35 "k8s.io/apimachinery/pkg/runtime/schema"
36 "k8s.io/cli-runtime/pkg/genericclioptions"
37 "k8s.io/cli-runtime/pkg/genericiooptions"
38 "k8s.io/cli-runtime/pkg/resource"
39 restclient "k8s.io/client-go/rest"
40 "k8s.io/client-go/rest/fake"
41 cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
42 "k8s.io/kubectl/pkg/scheme"
43 )
44
45 func TestSetEnvLocal(t *testing.T) {
46 tf := cmdtesting.NewTestFactory().WithNamespace("test")
47 defer tf.Cleanup()
48
49 tf.Client = &fake.RESTClient{
50 GroupVersion: schema.GroupVersion{Version: ""},
51 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
52 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
53 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
54 return nil, nil
55 }),
56 }
57 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
58 outputFormat := "name"
59
60 streams, _, buf, bufErr := genericiooptions.NewTestIOStreams()
61 opts := NewEnvOptions(streams)
62 opts.PrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme)
63 opts.FilenameOptions = resource.FilenameOptions{
64 Filenames: []string{"../../../testdata/controller.yaml"},
65 }
66 opts.Local = true
67
68 err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"env=prod"})
69 assert.NoError(t, err)
70 err = opts.Validate()
71 assert.NoError(t, err)
72 err = opts.RunEnv()
73 assert.NoError(t, err)
74 if bufErr.Len() > 0 {
75 t.Errorf("unexpected error: %s", bufErr.String())
76 }
77 if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
78 t.Errorf("did not set env: %s", buf.String())
79 }
80 }
81
82 func TestSetEnvLocalNamespace(t *testing.T) {
83 tf := cmdtesting.NewTestFactory()
84 defer tf.Cleanup()
85
86 tf.Client = &fake.RESTClient{
87 GroupVersion: schema.GroupVersion{Version: ""},
88 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
89 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
90 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
91 return nil, nil
92 }),
93 }
94 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
95 outputFormat := "yaml"
96
97 streams, _, buf, bufErr := genericiooptions.NewTestIOStreams()
98 opts := NewEnvOptions(streams)
99 opts.PrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme)
100 opts.FilenameOptions = resource.FilenameOptions{
101 Filenames: []string{"../../../testdata/set/namespaced-resource.yaml"},
102 }
103 opts.Local = true
104
105 err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"env=prod"})
106 assert.NoError(t, err)
107 err = opts.Validate()
108 assert.NoError(t, err)
109 err = opts.RunEnv()
110 assert.NoError(t, err)
111 if bufErr.Len() > 0 {
112 t.Errorf("unexpected error: %s", bufErr.String())
113 }
114 if !strings.Contains(buf.String(), "namespace: existing-ns") {
115 t.Errorf("did not set env: %s", buf.String())
116 }
117 }
118
119 func TestSetMultiResourcesEnvLocal(t *testing.T) {
120 tf := cmdtesting.NewTestFactory().WithNamespace("test")
121 defer tf.Cleanup()
122
123 tf.Client = &fake.RESTClient{
124 GroupVersion: schema.GroupVersion{Version: ""},
125 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
126 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
127 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
128 return nil, nil
129 }),
130 }
131 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
132
133 outputFormat := "name"
134 streams, _, buf, bufErr := genericiooptions.NewTestIOStreams()
135 opts := NewEnvOptions(streams)
136 opts.PrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme)
137 opts.FilenameOptions = resource.FilenameOptions{
138 Filenames: []string{"../../../testdata/set/multi-resource-yaml.yaml"},
139 }
140 opts.Local = true
141
142 err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"env=prod"})
143 assert.NoError(t, err)
144 err = opts.Validate()
145 assert.NoError(t, err)
146 err = opts.RunEnv()
147 assert.NoError(t, err)
148 if bufErr.Len() > 0 {
149 t.Errorf("unexpected error: %s", bufErr.String())
150 }
151 expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
152 if buf.String() != expectedOut {
153 t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
154 }
155 }
156
157 func TestSetEnvRemote(t *testing.T) {
158 inputs := []struct {
159 name string
160 object runtime.Object
161 groupVersion schema.GroupVersion
162 path string
163 args []string
164 }{
165 {
166 name: "test extensions.v1beta1 replicaset",
167 object: &extensionsv1beta1.ReplicaSet{
168 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
169 Spec: extensionsv1beta1.ReplicaSetSpec{
170 Template: corev1.PodTemplateSpec{
171 Spec: corev1.PodSpec{
172 Containers: []corev1.Container{
173 {
174 Name: "nginx",
175 Image: "nginx",
176 },
177 },
178 },
179 },
180 },
181 },
182 groupVersion: extensionsv1beta1.SchemeGroupVersion,
183 path: "/namespaces/test/replicasets/nginx",
184 args: []string{"replicaset", "nginx", "env=prod"},
185 },
186 {
187 name: "test apps.v1beta2 replicaset",
188 object: &appsv1beta2.ReplicaSet{
189 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
190 Spec: appsv1beta2.ReplicaSetSpec{
191 Template: corev1.PodTemplateSpec{
192 Spec: corev1.PodSpec{
193 Containers: []corev1.Container{
194 {
195 Name: "nginx",
196 Image: "nginx",
197 },
198 },
199 },
200 },
201 },
202 },
203 groupVersion: appsv1beta2.SchemeGroupVersion,
204 path: "/namespaces/test/replicasets/nginx",
205 args: []string{"replicaset", "nginx", "env=prod"},
206 },
207 {
208 name: "test appsv1 replicaset",
209 object: &appsv1.ReplicaSet{
210 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
211 Spec: appsv1.ReplicaSetSpec{
212 Template: corev1.PodTemplateSpec{
213 Spec: corev1.PodSpec{
214 Containers: []corev1.Container{
215 {
216 Name: "nginx",
217 Image: "nginx",
218 },
219 },
220 },
221 },
222 },
223 },
224 groupVersion: appsv1.SchemeGroupVersion,
225 path: "/namespaces/test/replicasets/nginx",
226 args: []string{"replicaset", "nginx", "env=prod"},
227 },
228 {
229 name: "test extensions.v1beta1 daemonset",
230 object: &extensionsv1beta1.DaemonSet{
231 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
232 Spec: extensionsv1beta1.DaemonSetSpec{
233 Template: corev1.PodTemplateSpec{
234 Spec: corev1.PodSpec{
235 Containers: []corev1.Container{
236 {
237 Name: "nginx",
238 Image: "nginx",
239 },
240 },
241 },
242 },
243 },
244 },
245 groupVersion: extensionsv1beta1.SchemeGroupVersion,
246 path: "/namespaces/test/daemonsets/nginx",
247 args: []string{"daemonset", "nginx", "env=prod"},
248 },
249 {
250 name: "test appsv1beta2 daemonset",
251 object: &appsv1beta2.DaemonSet{
252 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
253 Spec: appsv1beta2.DaemonSetSpec{
254 Template: corev1.PodTemplateSpec{
255 Spec: corev1.PodSpec{
256 Containers: []corev1.Container{
257 {
258 Name: "nginx",
259 Image: "nginx",
260 },
261 },
262 },
263 },
264 },
265 },
266 groupVersion: appsv1beta2.SchemeGroupVersion,
267 path: "/namespaces/test/daemonsets/nginx",
268 args: []string{"daemonset", "nginx", "env=prod"},
269 },
270 {
271 name: "test appsv1 daemonset",
272 object: &appsv1.DaemonSet{
273 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
274 Spec: appsv1.DaemonSetSpec{
275 Template: corev1.PodTemplateSpec{
276 Spec: corev1.PodSpec{
277 Containers: []corev1.Container{
278 {
279 Name: "nginx",
280 Image: "nginx",
281 },
282 },
283 },
284 },
285 },
286 },
287 groupVersion: appsv1.SchemeGroupVersion,
288 path: "/namespaces/test/daemonsets/nginx",
289 args: []string{"daemonset", "nginx", "env=prod"},
290 },
291 {
292 name: "test extensions.v1beta1 deployment",
293 object: &extensionsv1beta1.Deployment{
294 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
295 Spec: extensionsv1beta1.DeploymentSpec{
296 Template: corev1.PodTemplateSpec{
297 Spec: corev1.PodSpec{
298 Containers: []corev1.Container{
299 {
300 Name: "nginx",
301 Image: "nginx",
302 },
303 },
304 },
305 },
306 },
307 },
308 groupVersion: extensionsv1beta1.SchemeGroupVersion,
309 path: "/namespaces/test/deployments/nginx",
310 args: []string{"deployment", "nginx", "env=prod"},
311 },
312 {
313 name: "test appsv1beta1 deployment",
314 object: &appsv1beta1.Deployment{
315 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
316 Spec: appsv1beta1.DeploymentSpec{
317 Template: corev1.PodTemplateSpec{
318 Spec: corev1.PodSpec{
319 Containers: []corev1.Container{
320 {
321 Name: "nginx",
322 Image: "nginx",
323 },
324 },
325 },
326 },
327 },
328 },
329 groupVersion: appsv1beta1.SchemeGroupVersion,
330 path: "/namespaces/test/deployments/nginx",
331 args: []string{"deployment", "nginx", "env=prod"},
332 },
333 {
334 name: "test appsv1beta2 deployment",
335 object: &appsv1beta2.Deployment{
336 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
337 Spec: appsv1beta2.DeploymentSpec{
338 Template: corev1.PodTemplateSpec{
339 Spec: corev1.PodSpec{
340 Containers: []corev1.Container{
341 {
342 Name: "nginx",
343 Image: "nginx",
344 },
345 },
346 },
347 },
348 },
349 },
350 groupVersion: appsv1beta2.SchemeGroupVersion,
351 path: "/namespaces/test/deployments/nginx",
352 args: []string{"deployment", "nginx", "env=prod"},
353 },
354 {
355 name: "test appsv1 deployment",
356 object: &appsv1.Deployment{
357 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
358 Spec: appsv1.DeploymentSpec{
359 Template: corev1.PodTemplateSpec{
360 Spec: corev1.PodSpec{
361 Containers: []corev1.Container{
362 {
363 Name: "nginx",
364 Image: "nginx",
365 },
366 },
367 },
368 },
369 },
370 },
371 groupVersion: appsv1.SchemeGroupVersion,
372 path: "/namespaces/test/deployments/nginx",
373 args: []string{"deployment", "nginx", "env=prod"},
374 },
375 {
376 name: "test appsv1beta1 statefulset",
377 object: &appsv1beta1.StatefulSet{
378 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
379 Spec: appsv1beta1.StatefulSetSpec{
380 Template: corev1.PodTemplateSpec{
381 Spec: corev1.PodSpec{
382 Containers: []corev1.Container{
383 {
384 Name: "nginx",
385 Image: "nginx",
386 },
387 },
388 },
389 },
390 },
391 },
392 groupVersion: appsv1beta1.SchemeGroupVersion,
393 path: "/namespaces/test/statefulsets/nginx",
394 args: []string{"statefulset", "nginx", "env=prod"},
395 },
396 {
397 name: "test appsv1beta2 statefulset",
398 object: &appsv1beta2.StatefulSet{
399 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
400 Spec: appsv1beta2.StatefulSetSpec{
401 Template: corev1.PodTemplateSpec{
402 Spec: corev1.PodSpec{
403 Containers: []corev1.Container{
404 {
405 Name: "nginx",
406 Image: "nginx",
407 },
408 },
409 },
410 },
411 },
412 },
413 groupVersion: appsv1beta2.SchemeGroupVersion,
414 path: "/namespaces/test/statefulsets/nginx",
415 args: []string{"statefulset", "nginx", "env=prod"},
416 },
417 {
418 name: "test appsv1 statefulset",
419 object: &appsv1.StatefulSet{
420 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
421 Spec: appsv1.StatefulSetSpec{
422 Template: corev1.PodTemplateSpec{
423 Spec: corev1.PodSpec{
424 Containers: []corev1.Container{
425 {
426 Name: "nginx",
427 Image: "nginx",
428 },
429 },
430 },
431 },
432 },
433 },
434 groupVersion: appsv1.SchemeGroupVersion,
435 path: "/namespaces/test/statefulsets/nginx",
436 args: []string{"statefulset", "nginx", "env=prod"},
437 },
438 {
439 name: "set image batchv1 CronJob",
440 object: &batchv1.CronJob{
441 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
442 Spec: batchv1.CronJobSpec{
443 JobTemplate: batchv1.JobTemplateSpec{
444 Spec: batchv1.JobSpec{
445 Template: corev1.PodTemplateSpec{
446 Spec: corev1.PodSpec{
447 Containers: []corev1.Container{
448 {
449 Name: "nginx",
450 Image: "nginx",
451 },
452 },
453 },
454 },
455 },
456 },
457 },
458 },
459 groupVersion: batchv1.SchemeGroupVersion,
460 path: "/namespaces/test/cronjobs/nginx",
461 args: []string{"cronjob", "nginx", "env=prod"},
462 },
463 {
464 name: "test corev1 replication controller",
465 object: &corev1.ReplicationController{
466 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
467 Spec: corev1.ReplicationControllerSpec{
468 Template: &corev1.PodTemplateSpec{
469 Spec: corev1.PodSpec{
470 Containers: []corev1.Container{
471 {
472 Name: "nginx",
473 Image: "nginx",
474 },
475 },
476 },
477 },
478 },
479 },
480 groupVersion: corev1.SchemeGroupVersion,
481 path: "/namespaces/test/replicationcontrollers/nginx",
482 args: []string{"replicationcontroller", "nginx", "env=prod"},
483 },
484 }
485 for _, input := range inputs {
486 t.Run(input.name, func(t *testing.T) {
487 tf := cmdtesting.NewTestFactory().WithNamespace("test")
488 defer tf.Cleanup()
489
490 tf.Client = &fake.RESTClient{
491 GroupVersion: input.groupVersion,
492 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
493 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
494 switch p, m := req.URL.Path, req.Method; {
495 case p == input.path && m == http.MethodGet:
496 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
497 case p == input.path && m == http.MethodPatch:
498 stream, err := req.GetBody()
499 if err != nil {
500 return nil, err
501 }
502 bytes, err := io.ReadAll(stream)
503 if err != nil {
504 return nil, err
505 }
506 assert.Contains(t, string(bytes), `"value":`+`"`+"prod"+`"`, fmt.Sprintf("env not updated for %#v", input.object))
507 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
508 default:
509 t.Errorf("%s: unexpected request: %s %#v\n%#v", "image", req.Method, req.URL, req)
510 return nil, fmt.Errorf("unexpected request")
511 }
512 }),
513 }
514
515 outputFormat := "yaml"
516 streams := genericiooptions.NewTestIOStreamsDiscard()
517 opts := NewEnvOptions(streams)
518 opts.PrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme)
519 opts.Local = false
520 opts.IOStreams = streams
521 err := opts.Complete(tf, NewCmdEnv(tf, streams), input.args)
522 assert.NoError(t, err)
523 err = opts.RunEnv()
524 assert.NoError(t, err)
525 })
526 }
527 }
528
529 func TestSetEnvFromResource(t *testing.T) {
530 mockConfigMap := &corev1.ConfigMap{
531 ObjectMeta: metav1.ObjectMeta{Name: "testconfigmap"},
532 Data: map[string]string{
533 "env": "prod",
534 "test-key": "testValue",
535 "test-key-two": "testValueTwo",
536 },
537 }
538
539 mockConfigMapUpperCaseKey := &corev1.ConfigMap{
540 ObjectMeta: metav1.ObjectMeta{Name: "testconfigmapuppercasekey"},
541 Data: map[string]string{
542 "ENV": "prod",
543 "TEST_KEY": "testValue",
544 "TEST_KEY_TWO": "testValueTwo",
545 },
546 }
547
548 mockSecret := &corev1.Secret{
549 ObjectMeta: metav1.ObjectMeta{Name: "testsecret"},
550 Data: map[string][]byte{
551 "env": []byte("prod"),
552 "test-key": []byte("testValue"),
553 "test-key-two": []byte("testValueTwo"),
554 },
555 }
556
557 mockSecretUpperCaseKey := &corev1.Secret{
558 ObjectMeta: metav1.ObjectMeta{Name: "testsecretuppercasekey"},
559 Data: map[string][]byte{
560 "ENV": []byte("prod"),
561 "TEST_KEY": []byte("testValue"),
562 "TEST_KEY_TWO": []byte("testValueTwo"),
563 },
564 }
565
566 inputs := []struct {
567 name string
568 args []string
569 from string
570 keys []string
571 assertIncludes []string
572 assertExcludes []string
573 warning bool
574 }{
575 {
576 name: "test from configmap",
577 args: []string{"deployment", "nginx"},
578 from: "configmap/testconfigmap",
579 keys: []string{},
580 assertIncludes: []string{
581 `{"name":"ENV","valueFrom":{"configMapKeyRef":{"key":"env","name":"testconfigmap"}}}`,
582 `{"name":"TEST_KEY","valueFrom":{"configMapKeyRef":{"key":"test-key","name":"testconfigmap"}}}`,
583 `{"name":"TEST_KEY_TWO","valueFrom":{"configMapKeyRef":{"key":"test-key-two","name":"testconfigmap"}}}`,
584 },
585 assertExcludes: []string{},
586 warning: true,
587 },
588 {
589 name: "test from configmap with upper case key",
590 args: []string{"deployment", "nginx"},
591 from: "configmap/testconfigmapuppercasekey",
592 keys: []string{},
593 assertIncludes: []string{
594 `{"name":"ENV","valueFrom":{"configMapKeyRef":{"key":"ENV","name":"testconfigmapuppercasekey"}}}`,
595 `{"name":"TEST_KEY","valueFrom":{"configMapKeyRef":{"key":"TEST_KEY","name":"testconfigmapuppercasekey"}}}`,
596 `{"name":"TEST_KEY_TWO","valueFrom":{"configMapKeyRef":{"key":"TEST_KEY_TWO","name":"testconfigmapuppercasekey"}}}`,
597 },
598 assertExcludes: []string{},
599 warning: false,
600 },
601 {
602 name: "test from secret",
603 args: []string{"deployment", "nginx"},
604 from: "secret/testsecret",
605 keys: []string{},
606 assertIncludes: []string{
607 `{"name":"ENV","valueFrom":{"secretKeyRef":{"key":"env","name":"testsecret"}}}`,
608 `{"name":"TEST_KEY","valueFrom":{"secretKeyRef":{"key":"test-key","name":"testsecret"}}}`,
609 `{"name":"TEST_KEY_TWO","valueFrom":{"secretKeyRef":{"key":"test-key-two","name":"testsecret"}}}`,
610 },
611 assertExcludes: []string{},
612 warning: true,
613 },
614 {
615 name: "test from secret with upper case key",
616 args: []string{"deployment", "nginx"},
617 from: "secret/testsecretuppercasekey",
618 keys: []string{},
619 assertIncludes: []string{
620 `{"name":"ENV","valueFrom":{"secretKeyRef":{"key":"ENV","name":"testsecretuppercasekey"}}}`,
621 `{"name":"TEST_KEY","valueFrom":{"secretKeyRef":{"key":"TEST_KEY","name":"testsecretuppercasekey"}}}`,
622 `{"name":"TEST_KEY_TWO","valueFrom":{"secretKeyRef":{"key":"TEST_KEY_TWO","name":"testsecretuppercasekey"}}}`,
623 },
624 assertExcludes: []string{},
625 warning: false,
626 },
627 {
628 name: "test from configmap with keys",
629 args: []string{"deployment", "nginx"},
630 from: "configmap/testconfigmap",
631 keys: []string{"env", "test-key-two"},
632 assertIncludes: []string{
633 `{"name":"ENV","valueFrom":{"configMapKeyRef":{"key":"env","name":"testconfigmap"}}}`,
634 `{"name":"TEST_KEY_TWO","valueFrom":{"configMapKeyRef":{"key":"test-key-two","name":"testconfigmap"}}}`,
635 },
636 assertExcludes: []string{`{"name":"TEST_KEY","valueFrom":{"configMapKeyRef":{"key":"test-key","name":"testconfigmap"}}}`},
637 warning: true,
638 },
639 {
640 name: "test from secret with keys",
641 args: []string{"deployment", "nginx"},
642 from: "secret/testsecret",
643 keys: []string{"env", "test-key-two"},
644 assertIncludes: []string{
645 `{"name":"ENV","valueFrom":{"secretKeyRef":{"key":"env","name":"testsecret"}}}`,
646 `{"name":"TEST_KEY_TWO","valueFrom":{"secretKeyRef":{"key":"test-key-two","name":"testsecret"}}}`,
647 },
648 assertExcludes: []string{`{"name":"TEST_KEY","valueFrom":{"secretKeyRef":{"key":"test-key","name":"testsecret"}}}`},
649 warning: true,
650 },
651 }
652
653 for _, input := range inputs {
654 mockDeployment := &appsv1.Deployment{
655 ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
656 Spec: appsv1.DeploymentSpec{
657 Template: corev1.PodTemplateSpec{
658 Spec: corev1.PodSpec{
659 Containers: []corev1.Container{
660 {
661 Name: "nginx",
662 Image: "nginx",
663 },
664 },
665 },
666 },
667 },
668 }
669 t.Run(input.name, func(t *testing.T) {
670 tf := cmdtesting.NewTestFactory().WithNamespace("test")
671 defer tf.Cleanup()
672
673 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
674 tf.Client = &fake.RESTClient{
675 GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
676 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
677 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
678 switch p, m := req.URL.Path, req.Method; {
679 case p == "/namespaces/test/configmaps/testconfigmap" && m == http.MethodGet:
680 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockConfigMap)}, nil
681 case p == "/namespaces/test/configmaps/testconfigmapuppercasekey" && m == http.MethodGet:
682 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockConfigMapUpperCaseKey)}, nil
683 case p == "/namespaces/test/secrets/testsecret" && m == http.MethodGet:
684 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockSecret)}, nil
685 case p == "/namespaces/test/secrets/testsecretuppercasekey" && m == http.MethodGet:
686 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockSecretUpperCaseKey)}, nil
687 case p == "/namespaces/test/deployments/nginx" && m == http.MethodGet:
688 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
689 case p == "/namespaces/test/deployments/nginx" && m == http.MethodPatch:
690 stream, err := req.GetBody()
691 if err != nil {
692 return nil, err
693 }
694 bytes, err := io.ReadAll(stream)
695 if err != nil {
696 return nil, err
697 }
698 for _, include := range input.assertIncludes {
699 assert.Contains(t, string(bytes), include)
700 }
701 for _, exclude := range input.assertExcludes {
702 assert.NotContains(t, string(bytes), exclude)
703 }
704 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
705 default:
706 t.Errorf("%s: unexpected request: %#v\n%#v", input.name, req.URL, req)
707 return nil, nil
708 }
709 }),
710 }
711
712 outputFormat := "yaml"
713 streams, _, _, errOut := genericiooptions.NewTestIOStreams()
714 opts := NewEnvOptions(streams)
715 opts.From = input.from
716 opts.Keys = input.keys
717 opts.PrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme)
718 opts.Local = false
719 opts.IOStreams = streams
720 err := opts.Complete(tf, NewCmdEnv(tf, streams), input.args)
721 assert.NoError(t, err)
722 err = opts.RunEnv()
723 if input.warning {
724 assert.Contains(t, errOut.String(), "Warning")
725 } else {
726 assert.NotContains(t, errOut.String(), "Warning")
727 }
728 assert.NoError(t, err)
729 })
730 }
731 }
732
733 func TestSetEnvRemoteWithSpecificContainers(t *testing.T) {
734 inputs := []struct {
735 name string
736 args []string
737 selector string
738
739 expectedContainers int
740 }{
741 {
742 name: "all containers",
743 args: []string{"deployments", "redis", "env=prod"},
744 selector: "*",
745 expectedContainers: 2,
746 },
747 {
748 name: "use wildcards to select some containers",
749 args: []string{"deployments", "redis", "env=prod"},
750 selector: "red*",
751 expectedContainers: 1,
752 },
753 {
754 name: "single container",
755 args: []string{"deployments", "redis", "env=prod"},
756 selector: "redis",
757 expectedContainers: 1,
758 },
759 }
760
761 for _, input := range inputs {
762 mockDeployment := &appsv1.Deployment{
763 ObjectMeta: metav1.ObjectMeta{
764 Name: "redis",
765 Namespace: "test",
766 },
767 Spec: appsv1.DeploymentSpec{
768 Template: corev1.PodTemplateSpec{
769 Spec: corev1.PodSpec{
770 InitContainers: []corev1.Container{
771 {
772 Name: "init",
773 Image: "redis",
774 },
775 },
776 Containers: []corev1.Container{
777 {
778 Name: "redis",
779 Image: "redis",
780 },
781 },
782 },
783 },
784 },
785 }
786 t.Run(input.name, func(t *testing.T) {
787 tf := cmdtesting.NewTestFactory().WithNamespace("test")
788 defer tf.Cleanup()
789 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
790 tf.Client = &fake.RESTClient{
791 GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
792 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
793 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
794 switch p, m := req.URL.Path, req.Method; {
795 case p == "/namespaces/test/deployments/redis" && m == http.MethodGet:
796 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
797 case p == "/namespaces/test/deployments/redis" && m == http.MethodPatch:
798 stream, err := req.GetBody()
799 if err != nil {
800 return nil, err
801 }
802 bytes, err := io.ReadAll(stream)
803 if err != nil {
804 return nil, err
805 }
806 updated := strings.Count(string(bytes), `"value":`+`"`+"prod"+`"`)
807 if updated != input.expectedContainers {
808 t.Errorf("expected %d containers to be selected but got %d \n", input.expectedContainers, updated)
809 }
810 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(mockDeployment)}, nil
811 default:
812 t.Errorf("%s: unexpected request: %#v\n%#v", input.name, req.URL, req)
813 return nil, nil
814 }
815 }),
816 }
817 streams := genericiooptions.NewTestIOStreamsDiscard()
818 opts := &EnvOptions{
819 PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput("yaml").WithTypeSetter(scheme.Scheme),
820 ContainerSelector: input.selector,
821 Overwrite: true,
822 IOStreams: streams,
823 }
824 err := opts.Complete(tf, NewCmdEnv(tf, streams), input.args)
825 assert.NoError(t, err)
826 err = opts.RunEnv()
827 assert.NoError(t, err)
828 })
829 }
830 }
831
832 func TestSetEnvDoubleStdinUsage(t *testing.T) {
833 tf := cmdtesting.NewTestFactory().WithNamespace("test")
834 defer tf.Cleanup()
835
836 tf.Client = &fake.RESTClient{
837 GroupVersion: schema.GroupVersion{Version: ""},
838 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
839 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
840 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
841 return nil, nil
842 }),
843 }
844 tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
845
846 streams, bufIn, _, _ := genericiooptions.NewTestIOStreams()
847 bufIn.WriteString("SOME_ENV_VAR_KEY=SOME_ENV_VAR_VAL")
848 opts := NewEnvOptions(streams)
849 opts.FilenameOptions = resource.FilenameOptions{
850 Filenames: []string{"-"},
851 }
852
853 err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"-"})
854 assert.NoError(t, err)
855 err = opts.Validate()
856 assert.NoError(t, err)
857 err = opts.RunEnv()
858 assert.ErrorIs(t, err, resource.StdinMultiUseError)
859 }
860
View as plain text