1
16
17 package label
18
19 import (
20 "bytes"
21 "fmt"
22 "io"
23 "net/http"
24 "reflect"
25 "strings"
26 "testing"
27
28 v1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/runtime"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 "k8s.io/apimachinery/pkg/util/json"
33 "k8s.io/cli-runtime/pkg/genericiooptions"
34 "k8s.io/cli-runtime/pkg/resource"
35 "k8s.io/client-go/rest/fake"
36 cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
37 "k8s.io/kubectl/pkg/scheme"
38 )
39
40 func TestValidateLabels(t *testing.T) {
41 tests := []struct {
42 meta *metav1.ObjectMeta
43 labels map[string]string
44 expectErr bool
45 test string
46 }{
47 {
48 meta: &metav1.ObjectMeta{
49 Labels: map[string]string{
50 "a": "b",
51 "c": "d",
52 },
53 },
54 labels: map[string]string{
55 "a": "c",
56 "d": "b",
57 },
58 test: "one shared",
59 expectErr: true,
60 },
61 {
62 meta: &metav1.ObjectMeta{
63 Labels: map[string]string{
64 "a": "b",
65 "c": "d",
66 },
67 },
68 labels: map[string]string{
69 "b": "d",
70 "c": "a",
71 },
72 test: "second shared",
73 expectErr: true,
74 },
75 {
76 meta: &metav1.ObjectMeta{
77 Labels: map[string]string{
78 "a": "b",
79 "c": "d",
80 },
81 },
82 labels: map[string]string{
83 "b": "a",
84 "d": "c",
85 },
86 test: "no overlap",
87 },
88 {
89 meta: &metav1.ObjectMeta{},
90 labels: map[string]string{
91 "b": "a",
92 "d": "c",
93 },
94 test: "no labels",
95 },
96 }
97 for _, test := range tests {
98 err := validateNoOverwrites(test.meta, test.labels)
99 if test.expectErr && err == nil {
100 t.Errorf("%s: unexpected non-error", test.test)
101 }
102 if !test.expectErr && err != nil {
103 t.Errorf("%s: unexpected error: %v", test.test, err)
104 }
105 }
106 }
107
108 func TestParseLabels(t *testing.T) {
109 tests := []struct {
110 labels []string
111 expected map[string]string
112 expectedRemove []string
113 expectErr bool
114 }{
115 {
116 labels: []string{"a=b", "c=d"},
117 expected: map[string]string{"a": "b", "c": "d"},
118 },
119 {
120 labels: []string{},
121 expected: map[string]string{},
122 },
123 {
124 labels: []string{"a=b", "c=d", "e-"},
125 expected: map[string]string{"a": "b", "c": "d"},
126 expectedRemove: []string{"e"},
127 },
128 {
129 labels: []string{"ab", "c=d"},
130 expectErr: true,
131 },
132 {
133 labels: []string{"a=b", "c=d", "a-"},
134 expectErr: true,
135 },
136 {
137 labels: []string{"a="},
138 expected: map[string]string{"a": ""},
139 },
140 {
141 labels: []string{"a=%^$"},
142 expectErr: true,
143 },
144 }
145 for _, test := range tests {
146 labels, remove, err := parseLabels(test.labels)
147 if test.expectErr && err == nil {
148 t.Errorf("unexpected non-error: %v", test)
149 }
150 if !test.expectErr && err != nil {
151 t.Errorf("unexpected error: %v %v", err, test)
152 }
153 if !reflect.DeepEqual(labels, test.expected) {
154 t.Errorf("expected: %v, got %v", test.expected, labels)
155 }
156 if !reflect.DeepEqual(remove, test.expectedRemove) {
157 t.Errorf("expected: %v, got %v", test.expectedRemove, remove)
158 }
159 }
160 }
161
162 func TestLabelFunc(t *testing.T) {
163 tests := []struct {
164 obj runtime.Object
165 overwrite bool
166 version string
167 labels map[string]string
168 remove []string
169 expected runtime.Object
170 expectErr string
171 }{
172 {
173 obj: &v1.Pod{
174 ObjectMeta: metav1.ObjectMeta{
175 Labels: map[string]string{"a": "b"},
176 },
177 },
178 labels: map[string]string{"a": "b"},
179 expected: &v1.Pod{
180 ObjectMeta: metav1.ObjectMeta{
181 Labels: map[string]string{"a": "b"},
182 },
183 },
184 },
185 {
186 obj: &v1.Pod{
187 ObjectMeta: metav1.ObjectMeta{
188 Labels: map[string]string{"a": "b"},
189 },
190 },
191 labels: map[string]string{"a": "c"},
192 expectErr: "'a' already has a value (b), and --overwrite is false",
193 },
194 {
195 obj: &v1.Pod{
196 ObjectMeta: metav1.ObjectMeta{
197 Labels: map[string]string{"a": "b"},
198 },
199 },
200 labels: map[string]string{"a": "c"},
201 overwrite: true,
202 expected: &v1.Pod{
203 ObjectMeta: metav1.ObjectMeta{
204 Labels: map[string]string{"a": "c"},
205 },
206 },
207 },
208 {
209 obj: &v1.Pod{
210 ObjectMeta: metav1.ObjectMeta{
211 Labels: map[string]string{"a": "b"},
212 },
213 },
214 labels: map[string]string{"c": "d"},
215 expected: &v1.Pod{
216 ObjectMeta: metav1.ObjectMeta{
217 Labels: map[string]string{"a": "b", "c": "d"},
218 },
219 },
220 },
221 {
222 obj: &v1.Pod{
223 ObjectMeta: metav1.ObjectMeta{
224 Labels: map[string]string{"a": "b"},
225 },
226 },
227 labels: map[string]string{"c": "d"},
228 version: "2",
229 expected: &v1.Pod{
230 ObjectMeta: metav1.ObjectMeta{
231 Labels: map[string]string{"a": "b", "c": "d"},
232 ResourceVersion: "2",
233 },
234 },
235 },
236 {
237 obj: &v1.Pod{
238 ObjectMeta: metav1.ObjectMeta{
239 Labels: map[string]string{"a": "b"},
240 },
241 },
242 labels: map[string]string{},
243 remove: []string{"a"},
244 expected: &v1.Pod{
245 ObjectMeta: metav1.ObjectMeta{
246 Labels: map[string]string{},
247 },
248 },
249 },
250 {
251 obj: &v1.Pod{
252 ObjectMeta: metav1.ObjectMeta{
253 Labels: map[string]string{"a": "b", "c": "d"},
254 },
255 },
256 labels: map[string]string{"e": "f"},
257 remove: []string{"a"},
258 expected: &v1.Pod{
259 ObjectMeta: metav1.ObjectMeta{
260 Labels: map[string]string{
261 "c": "d",
262 "e": "f",
263 },
264 },
265 },
266 },
267 {
268 obj: &v1.Pod{
269 ObjectMeta: metav1.ObjectMeta{},
270 },
271 labels: map[string]string{"a": "b"},
272 expected: &v1.Pod{
273 ObjectMeta: metav1.ObjectMeta{
274 Labels: map[string]string{"a": "b"},
275 },
276 },
277 },
278 }
279 for _, test := range tests {
280 err := labelFunc(test.obj, test.overwrite, test.version, test.labels, test.remove)
281 if test.expectErr != "" {
282 if err == nil {
283 t.Errorf("unexpected non-error: %v", test)
284 }
285 if err.Error() != test.expectErr {
286 t.Errorf("error expected: %v, got: %v", test.expectErr, err.Error())
287 }
288 continue
289 }
290 if test.expectErr == "" && err != nil {
291 t.Errorf("unexpected error: %v %v", err, test)
292 }
293 if !reflect.DeepEqual(test.obj, test.expected) {
294 t.Errorf("expected: %v, got %v", test.expected, test.obj)
295 }
296 }
297 }
298
299 func TestLabelErrors(t *testing.T) {
300 testCases := map[string]struct {
301 args []string
302 errFn func(error) bool
303 }{
304 "no args": {
305 args: []string{},
306 errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
307 },
308 "not enough labels": {
309 args: []string{"pods"},
310 errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") },
311 },
312 "wrong labels": {
313 args: []string{"pods", "-"},
314 errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") },
315 },
316 "wrong labels 2": {
317 args: []string{"pods", "=bar"},
318 errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") },
319 },
320 "no resources": {
321 args: []string{"pods-"},
322 errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
323 },
324 "no resources 2": {
325 args: []string{"pods=bar"},
326 errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
327 },
328 "resources but no selectors": {
329 args: []string{"pods", "app=bar"},
330 errFn: func(err error) bool {
331 return strings.Contains(err.Error(), "resource(s) were provided, but no name was specified")
332 },
333 },
334 "multiple resources but no selectors": {
335 args: []string{"pods,deployments", "app=bar"},
336 errFn: func(err error) bool {
337 return strings.Contains(err.Error(), "resource(s) were provided, but no name was specified")
338 },
339 },
340 }
341
342 for k, testCase := range testCases {
343 t.Run(k, func(t *testing.T) {
344 tf := cmdtesting.NewTestFactory().WithNamespace("test")
345 defer tf.Cleanup()
346
347 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
348
349 ioStreams, _, _, _ := genericiooptions.NewTestIOStreams()
350 buf := bytes.NewBuffer([]byte{})
351 cmd := NewCmdLabel(tf, ioStreams)
352 cmd.SetOut(buf)
353 cmd.SetErr(buf)
354
355 opts := NewLabelOptions(ioStreams)
356 err := opts.Complete(tf, cmd, testCase.args)
357 if err == nil {
358 err = opts.Validate()
359 }
360 if err == nil {
361 err = opts.RunLabel()
362 }
363 if !testCase.errFn(err) {
364 t.Errorf("%s: unexpected error: %v", k, err)
365 return
366 }
367 if buf.Len() > 0 {
368 t.Errorf("buffer should be empty: %s", buf.String())
369 }
370 })
371 }
372 }
373
374 func TestLabelForResourceFromFile(t *testing.T) {
375 pods, _, _ := cmdtesting.TestData()
376 tf := cmdtesting.NewTestFactory().WithNamespace("test")
377 defer tf.Cleanup()
378
379 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
380
381 tf.UnstructuredClient = &fake.RESTClient{
382 NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
383 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
384 switch req.Method {
385 case "GET":
386 switch req.URL.Path {
387 case "/namespaces/test/replicationcontrollers/cassandra":
388 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
389 default:
390 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
391 return nil, nil
392 }
393 case "PATCH":
394 switch req.URL.Path {
395 case "/namespaces/test/replicationcontrollers/cassandra":
396 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
397 default:
398 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
399 return nil, nil
400 }
401 default:
402 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
403 return nil, nil
404 }
405 }),
406 }
407 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
408
409 ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
410 cmd := NewCmdLabel(tf, ioStreams)
411 opts := NewLabelOptions(ioStreams)
412 opts.Filenames = []string{"../../../testdata/controller.yaml"}
413 err := opts.Complete(tf, cmd, []string{"a=b"})
414 if err == nil {
415 err = opts.Validate()
416 }
417 if err == nil {
418 err = opts.RunLabel()
419 }
420 if err != nil {
421 t.Fatalf("unexpected error: %v", err)
422 }
423 if !strings.Contains(buf.String(), "labeled") {
424 t.Errorf("did not set labels: %s", buf.String())
425 }
426 }
427
428 func TestLabelLocal(t *testing.T) {
429 tf := cmdtesting.NewTestFactory().WithNamespace("test")
430 defer tf.Cleanup()
431
432 tf.UnstructuredClient = &fake.RESTClient{
433 NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
434 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
435 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
436 return nil, nil
437 }),
438 }
439 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
440
441 ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
442 cmd := NewCmdLabel(tf, ioStreams)
443 opts := NewLabelOptions(ioStreams)
444 opts.Filenames = []string{"../../../testdata/controller.yaml"}
445 opts.local = true
446 err := opts.Complete(tf, cmd, []string{"a=b"})
447 if err == nil {
448 err = opts.Validate()
449 }
450 if err == nil {
451 err = opts.RunLabel()
452 }
453 if err != nil {
454 t.Fatalf("unexpected error: %v", err)
455 }
456 if !strings.Contains(buf.String(), "labeled") {
457 t.Errorf("did not set labels: %s", buf.String())
458 }
459 }
460
461 func TestLabelMultipleObjects(t *testing.T) {
462 pods, _, _ := cmdtesting.TestData()
463 tf := cmdtesting.NewTestFactory().WithNamespace("test")
464 defer tf.Cleanup()
465
466 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
467
468 tf.UnstructuredClient = &fake.RESTClient{
469 NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
470 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
471 switch req.Method {
472 case "GET":
473 switch req.URL.Path {
474 case "/namespaces/test/pods":
475 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, nil
476 default:
477 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
478 return nil, nil
479 }
480 case "PATCH":
481 switch req.URL.Path {
482 case "/namespaces/test/pods/foo":
483 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
484 case "/namespaces/test/pods/bar":
485 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[1])}, nil
486 default:
487 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
488 return nil, nil
489 }
490 default:
491 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
492 return nil, nil
493 }
494 }),
495 }
496 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
497
498 ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
499 opts := NewLabelOptions(ioStreams)
500 opts.all = true
501 cmd := NewCmdLabel(tf, ioStreams)
502 err := opts.Complete(tf, cmd, []string{"pods", "a=b"})
503 if err == nil {
504 err = opts.Validate()
505 }
506 if err == nil {
507 err = opts.RunLabel()
508 }
509 if err != nil {
510 t.Fatalf("unexpected error: %v", err)
511 }
512 if strings.Count(buf.String(), "labeled") != len(pods.Items) {
513 t.Errorf("not all labels are set: %s", buf.String())
514 }
515 }
516
517 func TestLabelResourceVersion(t *testing.T) {
518 tf := cmdtesting.NewTestFactory().WithNamespace("test")
519 defer tf.Cleanup()
520
521 tf.UnstructuredClient = &fake.RESTClient{
522 GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
523 NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
524 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
525 switch req.Method {
526 case "GET":
527 switch req.URL.Path {
528 case "/namespaces/test/pods/foo":
529 return &http.Response{
530 StatusCode: http.StatusOK,
531 Header: cmdtesting.DefaultHeader(),
532 Body: io.NopCloser(bytes.NewBufferString(
533 `{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"test","resourceVersion":"10"}}`,
534 ))}, nil
535 default:
536 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
537 return nil, nil
538 }
539 case "PATCH":
540 switch req.URL.Path {
541 case "/namespaces/test/pods/foo":
542 body, err := io.ReadAll(req.Body)
543 if err != nil {
544 t.Fatal(err)
545 }
546 if !bytes.Equal(body, []byte(`{"metadata":{"labels":{"a":"b"},"resourceVersion":"10"}}`)) {
547 t.Fatalf("expected patch with resourceVersion set, got %s", string(body))
548 }
549 return &http.Response{
550 StatusCode: http.StatusOK,
551 Header: cmdtesting.DefaultHeader(),
552 Body: io.NopCloser(bytes.NewBufferString(
553 `{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"test","resourceVersion":"11"}}`,
554 ))}, nil
555 default:
556 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
557 return nil, nil
558 }
559 default:
560 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
561 return nil, nil
562 }
563 }),
564 }
565 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
566
567 iostreams, _, bufOut, _ := genericiooptions.NewTestIOStreams()
568 cmd := NewCmdLabel(tf, iostreams)
569 cmd.SetOut(bufOut)
570 cmd.SetErr(bufOut)
571 options := NewLabelOptions(iostreams)
572 options.resourceVersion = "10"
573 args := []string{"pods/foo", "a=b"}
574 if err := options.Complete(tf, cmd, args); err != nil {
575 t.Fatalf("unexpected error: %v", err)
576 }
577 if err := options.Validate(); err != nil {
578 t.Fatalf("unexpected error: %v", err)
579 }
580 if err := options.RunLabel(); err != nil {
581 t.Fatalf("unexpected error: %v", err)
582 }
583 }
584
585 func TestRunLabelMsg(t *testing.T) {
586 tf := cmdtesting.NewTestFactory().WithNamespace("test")
587 defer tf.Cleanup()
588
589 tf.UnstructuredClient = &fake.RESTClient{
590 GroupVersion: schema.GroupVersion{Group: "testgroup", Version: "v1"},
591 NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
592 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
593 switch req.Method {
594 case "GET":
595 switch req.URL.Path {
596 case "/namespaces/test/pods/foo":
597 return &http.Response{
598 StatusCode: http.StatusOK,
599 Header: cmdtesting.DefaultHeader(),
600 Body: io.NopCloser(bytes.NewBufferString(
601 `{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"test","labels":{"existing":"abc"}}}`,
602 ))}, nil
603 default:
604 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
605 return nil, nil
606 }
607 case "PATCH":
608 switch req.URL.Path {
609 case "/namespaces/test/pods/foo":
610 return &http.Response{
611 StatusCode: http.StatusOK,
612 Header: cmdtesting.DefaultHeader(),
613 Body: io.NopCloser(bytes.NewBufferString(
614 `{"kind":"Pod","apiVersion":"v1","metadata":{"name":"foo","namespace":"test","labels":{"existing":"abc"}}}`,
615 ))}, nil
616 default:
617 t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
618 return nil, nil
619 }
620 default:
621 t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
622 return nil, nil
623 }
624 }),
625 }
626 tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
627
628 testCases := []struct {
629 name string
630 args []string
631 overwrite bool
632 dryRun string
633 expectedOut string
634 expectedError error
635 }{
636 {
637 name: "set new label",
638 args: []string{"pods/foo", "foo=bar"},
639 expectedOut: "pod/foo labeled\n",
640 },
641 {
642 name: "attempt to set existing label without using overwrite flag",
643 args: []string{"pods/foo", "existing=bar"},
644 expectedError: fmt.Errorf("'existing' already has a value (abc), and --overwrite is false"),
645 },
646 {
647 name: "set existing label",
648 args: []string{"pods/foo", "existing=bar"},
649 overwrite: true,
650 expectedOut: "pod/foo labeled\n",
651 },
652 {
653 name: "unset existing label",
654 args: []string{"pods/foo", "existing-"},
655 expectedOut: "pod/foo unlabeled\n",
656 },
657 {
658 name: "unset nonexisting label",
659 args: []string{"pods/foo", "foo-"},
660 expectedOut: `label "foo" not found.
661 pod/foo not labeled
662 `,
663 },
664 {
665 name: "set new label with server dry run",
666 args: []string{"pods/foo", "foo=bar"},
667 dryRun: "server",
668 expectedOut: "pod/foo labeled (server dry run)\n",
669 },
670 {
671 name: "set new label with client dry run",
672 args: []string{"pods/foo", "foo=bar"},
673 dryRun: "client",
674 expectedOut: "pod/foo labeled (dry run)\n",
675 },
676 {
677 name: "unset existing label with server dry run",
678 args: []string{"pods/foo", "existing-"},
679 dryRun: "server",
680 expectedOut: "pod/foo unlabeled (server dry run)\n",
681 },
682 {
683 name: "unset existing label with client dry run",
684 args: []string{"pods/foo", "existing-"},
685 dryRun: "client",
686 expectedOut: "pod/foo unlabeled (dry run)\n",
687 },
688 }
689
690 for _, tc := range testCases {
691 t.Run(tc.name, func(t *testing.T) {
692 iostreams, _, bufOut, _ := genericiooptions.NewTestIOStreams()
693 cmd := NewCmdLabel(tf, iostreams)
694 cmd.SetOut(bufOut)
695 cmd.SetErr(bufOut)
696 if tc.dryRun != "" {
697 cmd.Flags().Set("dry-run", tc.dryRun)
698 }
699 options := NewLabelOptions(iostreams)
700 if tc.overwrite {
701 options.overwrite = true
702 }
703 if err := options.Complete(tf, cmd, tc.args); err != nil {
704 t.Fatalf("unexpected error: %v", err)
705 }
706 if err := options.Validate(); err != nil {
707 t.Fatalf("unexpected error: %v", err)
708 }
709
710 err := options.RunLabel()
711 if tc.expectedError == nil {
712 if err != nil {
713 t.Fatalf("unexpected error: %v", err)
714 }
715 } else {
716 if err == nil {
717 t.Fatalf("expected, but did not get, error: %s", tc.expectedError.Error())
718 } else if err.Error() != tc.expectedError.Error() {
719 t.Fatalf("wrong error\ngot: %s\nexpected: %s\n", err.Error(), tc.expectedError.Error())
720 }
721 }
722
723 if bufOut.String() != tc.expectedOut {
724 t.Fatalf("wrong output\ngot:\n%s\nexpected:\n%s\n", bufOut.String(), tc.expectedOut)
725 }
726 })
727 }
728 }
729
730 func TestLabelMsg(t *testing.T) {
731 tests := []struct {
732 obj runtime.Object
733 overwrite bool
734 resourceVersion string
735 labels map[string]string
736 remove []string
737 expectObj runtime.Object
738 expectMsg string
739 expectErr bool
740 }{
741 {
742 obj: &v1.Pod{
743 ObjectMeta: metav1.ObjectMeta{
744 Labels: map[string]string{"a": "b"},
745 },
746 },
747 labels: map[string]string{"a": "b"},
748 expectMsg: MsgNotLabeled,
749 },
750 {
751 obj: &v1.Pod{
752 ObjectMeta: metav1.ObjectMeta{},
753 },
754 labels: map[string]string{"a": "b"},
755 expectObj: &v1.Pod{
756 ObjectMeta: metav1.ObjectMeta{
757 Labels: map[string]string{"a": "b"},
758 },
759 },
760 expectMsg: MsgLabeled,
761 },
762 {
763 obj: &v1.Pod{
764 ObjectMeta: metav1.ObjectMeta{
765 Labels: map[string]string{"a": "b"},
766 },
767 },
768 labels: map[string]string{"a": "c"},
769 overwrite: true,
770 expectObj: &v1.Pod{
771 ObjectMeta: metav1.ObjectMeta{
772 Labels: map[string]string{"a": "c"},
773 },
774 },
775 expectMsg: MsgLabeled,
776 },
777 {
778 obj: &v1.Pod{
779 ObjectMeta: metav1.ObjectMeta{
780 Labels: map[string]string{"a": "b"},
781 },
782 },
783 labels: map[string]string{"c": "d"},
784 expectObj: &v1.Pod{
785 ObjectMeta: metav1.ObjectMeta{
786 Labels: map[string]string{"a": "b", "c": "d"},
787 },
788 },
789 expectMsg: MsgLabeled,
790 },
791 {
792 obj: &v1.Pod{
793 ObjectMeta: metav1.ObjectMeta{
794 Labels: map[string]string{"a": "b"},
795 },
796 },
797 labels: map[string]string{"c": "d"},
798 resourceVersion: "2",
799 expectObj: &v1.Pod{
800 ObjectMeta: metav1.ObjectMeta{
801 Labels: map[string]string{"a": "b", "c": "d"},
802 ResourceVersion: "2",
803 },
804 },
805 expectMsg: MsgLabeled,
806 },
807 {
808 obj: &v1.Pod{
809 ObjectMeta: metav1.ObjectMeta{
810 Labels: map[string]string{"a": "b"},
811 },
812 },
813 labels: map[string]string{},
814 remove: []string{"a"},
815 expectObj: &v1.Pod{
816 ObjectMeta: metav1.ObjectMeta{
817 Labels: map[string]string{},
818 },
819 },
820 expectMsg: MsgUnLabeled,
821 },
822 {
823 obj: &v1.Pod{
824 ObjectMeta: metav1.ObjectMeta{
825 Labels: map[string]string{"a": "b", "c": "d"},
826 },
827 },
828 labels: map[string]string{"e": "f"},
829 remove: []string{"a"},
830 expectObj: &v1.Pod{
831 ObjectMeta: metav1.ObjectMeta{
832 Labels: map[string]string{
833 "c": "d",
834 "e": "f",
835 },
836 },
837 },
838 expectMsg: MsgLabeled,
839 },
840 {
841 obj: &v1.Pod{
842 ObjectMeta: metav1.ObjectMeta{
843 Labels: map[string]string{"status": "unhealthy"},
844 },
845 },
846 labels: map[string]string{"status": "healthy"},
847 overwrite: true,
848 expectObj: &v1.Pod{
849 ObjectMeta: metav1.ObjectMeta{
850 Labels: map[string]string{
851 "status": "healthy",
852 },
853 },
854 },
855 expectMsg: MsgLabeled,
856 },
857 {
858 obj: &v1.Pod{
859 ObjectMeta: metav1.ObjectMeta{
860 Labels: map[string]string{"status": "unhealthy"},
861 },
862 },
863 labels: map[string]string{"status": "healthy"},
864 overwrite: false,
865 expectObj: &v1.Pod{
866 ObjectMeta: metav1.ObjectMeta{
867 Labels: map[string]string{
868 "status": "unhealthy",
869 },
870 },
871 },
872 expectMsg: MsgNotLabeled,
873 expectErr: true,
874 },
875 }
876
877 for _, test := range tests {
878 oldData, err := json.Marshal(test.obj)
879 if err != nil {
880 t.Errorf("unexpected error: %v %v", err, test)
881 }
882
883 err = labelFunc(test.obj, test.overwrite, test.resourceVersion, test.labels, test.remove)
884 if test.expectErr && err == nil {
885 t.Errorf("unexpected non-error: %v", test)
886 continue
887 }
888 if !test.expectErr && err != nil {
889 t.Errorf("unexpected error: %v %v", err, test)
890 }
891
892 newObj, err := json.Marshal(test.obj)
893 if err != nil {
894 t.Errorf("unexpected error: %v %v", err, test)
895 }
896
897 dataChangeMsg := updateDataChangeMsg(oldData, newObj, test.overwrite)
898 if dataChangeMsg != test.expectMsg {
899 t.Errorf("unexpected dataChangeMsg: %v != %v, %v", dataChangeMsg, test.expectMsg, test)
900 }
901 }
902 }
903
View as plain text