1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package k8s_test
16
17 import (
18 "reflect"
19 "testing"
20
21 corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
23 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
24
25 "github.com/google/go-cmp/cmp"
26 corev1 "k8s.io/api/core/v1"
27 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 )
29
30 func TestSetDefaultHierarchicalReference(t *testing.T) {
31 tests := []struct {
32 name string
33 resource *k8s.Resource
34 ns *corev1.Namespace
35 hierarchicalRefs []corekccv1alpha1.HierarchicalReference
36 containers []corekccv1alpha1.Container
37
38 expectedResource *k8s.Resource
39 shouldErr bool
40 }{
41 {
42 name: "no defaulting if resource doesn't support hierarchical references",
43 resource: &k8s.Resource{
44 ObjectMeta: v1.ObjectMeta{
45 Annotations: map[string]string{
46 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
47 },
48 },
49 Spec: map[string]interface{}{
50 "field": "val",
51 },
52 },
53 ns: &corev1.Namespace{
54 ObjectMeta: v1.ObjectMeta{
55 Annotations: map[string]string{
56 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
57 },
58 Name: "namespace-name",
59 },
60 },
61 containers: []corekccv1alpha1.Container{
62 {Type: corekccv1alpha1.ContainerTypeProject},
63 },
64 expectedResource: &k8s.Resource{
65 ObjectMeta: v1.ObjectMeta{
66 Annotations: map[string]string{
67 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
68 },
69 },
70 Spec: map[string]interface{}{
71 "field": "val",
72 },
73 },
74 },
75 {
76 name: "no defaulting if resource specifies a reference already",
77 resource: &k8s.Resource{
78 ObjectMeta: v1.ObjectMeta{
79 Annotations: map[string]string{
80 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
81 },
82 },
83 Spec: map[string]interface{}{
84 "field": "val",
85 "projectRef": map[string]interface{}{
86 "name": "project-id-from-spec",
87 },
88 },
89 },
90 ns: &corev1.Namespace{
91 ObjectMeta: v1.ObjectMeta{
92 Annotations: map[string]string{
93 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
94 },
95 Name: "namespace-name",
96 },
97 },
98 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
99 {
100 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
101 Key: "projectRef",
102 },
103 },
104 containers: []corekccv1alpha1.Container{
105 {Type: corekccv1alpha1.ContainerTypeProject},
106 },
107 expectedResource: &k8s.Resource{
108 ObjectMeta: v1.ObjectMeta{
109 Annotations: map[string]string{
110 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
111 },
112 },
113 Spec: map[string]interface{}{
114 "field": "val",
115 "projectRef": map[string]interface{}{
116 "name": "project-id-from-spec",
117 },
118 },
119 },
120 },
121 {
122 name: "default from resource annotation if specified and supported over namespace annotation of same type",
123 resource: &k8s.Resource{
124 ObjectMeta: v1.ObjectMeta{
125 Annotations: map[string]string{
126 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
127 },
128 },
129 Spec: map[string]interface{}{
130 "field": "val",
131 },
132 },
133 ns: &corev1.Namespace{
134 ObjectMeta: v1.ObjectMeta{
135 Annotations: map[string]string{
136 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
137 },
138 Name: "namespace-name",
139 },
140 },
141 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
142 {
143 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
144 Key: "projectRef",
145 },
146 },
147 containers: []corekccv1alpha1.Container{
148 {Type: corekccv1alpha1.ContainerTypeProject},
149 },
150 expectedResource: &k8s.Resource{
151 ObjectMeta: v1.ObjectMeta{
152 Annotations: map[string]string{
153 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
154 },
155 },
156 Spec: map[string]interface{}{
157 "field": "val",
158 "projectRef": map[string]interface{}{
159 "external": "project-id-from-resource-annotation",
160 },
161 },
162 },
163 },
164 {
165 name: "default from resource annotation if specified and supported over namespace annotation of different type",
166 resource: &k8s.Resource{
167 ObjectMeta: v1.ObjectMeta{
168 Annotations: map[string]string{
169 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
170 },
171 },
172 Spec: map[string]interface{}{
173 "field": "val",
174 },
175 },
176 ns: &corev1.Namespace{
177 ObjectMeta: v1.ObjectMeta{
178 Annotations: map[string]string{
179 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
180 },
181 Name: "namespace-name",
182 },
183 },
184 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
185 {
186 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
187 Key: "projectRef",
188 },
189 {
190 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
191 Key: "folderRef",
192 },
193 },
194 containers: []corekccv1alpha1.Container{
195 {Type: corekccv1alpha1.ContainerTypeProject},
196 {Type: corekccv1alpha1.ContainerTypeFolder},
197 },
198 expectedResource: &k8s.Resource{
199 ObjectMeta: v1.ObjectMeta{
200 Annotations: map[string]string{
201 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
202 },
203 },
204 Spec: map[string]interface{}{
205 "field": "val",
206 "folderRef": map[string]interface{}{
207 "external": "folder-id-from-resource-annotation",
208 },
209 },
210 },
211 },
212 {
213 name: "default from resource annotation if specified and supported over namespace name",
214 resource: &k8s.Resource{
215 ObjectMeta: v1.ObjectMeta{
216 Annotations: map[string]string{
217 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
218 },
219 },
220 Spec: map[string]interface{}{
221 "field": "val",
222 },
223 },
224 ns: &corev1.Namespace{
225 ObjectMeta: v1.ObjectMeta{
226 Name: "namespace-name",
227 },
228 },
229 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
230 {
231 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
232 Key: "projectRef",
233 },
234 },
235 containers: []corekccv1alpha1.Container{
236 {Type: corekccv1alpha1.ContainerTypeProject},
237 },
238 expectedResource: &k8s.Resource{
239 ObjectMeta: v1.ObjectMeta{
240 Annotations: map[string]string{
241 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
242 },
243 },
244 Spec: map[string]interface{}{
245 "field": "val",
246 "projectRef": map[string]interface{}{
247 "external": "project-id-from-resource-annotation",
248 },
249 },
250 },
251 },
252 {
253 name: "default from namespace annotation if resource annotations are not supported",
254 resource: &k8s.Resource{
255 ObjectMeta: v1.ObjectMeta{
256 Annotations: map[string]string{
257 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
258 },
259 },
260 Spec: map[string]interface{}{
261 "field": "val",
262 },
263 },
264 ns: &corev1.Namespace{
265 ObjectMeta: v1.ObjectMeta{
266 Annotations: map[string]string{
267 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
268 },
269 Name: "namespace-name",
270 },
271 },
272 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
273 {
274 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
275 Key: "projectRef",
276 },
277 },
278 expectedResource: &k8s.Resource{
279 ObjectMeta: v1.ObjectMeta{
280 Annotations: map[string]string{
281 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
282 },
283 },
284 Spec: map[string]interface{}{
285 "field": "val",
286 "projectRef": map[string]interface{}{
287 "external": "project-id-from-namespace-annotation",
288 },
289 },
290 },
291 },
292 {
293 name: "default from namespace annotation if no resource annotation specified",
294 resource: &k8s.Resource{
295 Spec: map[string]interface{}{
296 "field": "val",
297 },
298 },
299 ns: &corev1.Namespace{
300 ObjectMeta: v1.ObjectMeta{
301 Annotations: map[string]string{
302 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
303 },
304 Name: "namespace-name",
305 },
306 },
307 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
308 {
309 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
310 Key: "projectRef",
311 },
312 },
313 containers: []corekccv1alpha1.Container{
314 {Type: corekccv1alpha1.ContainerTypeProject},
315 },
316 expectedResource: &k8s.Resource{
317 Spec: map[string]interface{}{
318 "field": "val",
319 "projectRef": map[string]interface{}{
320 "external": "project-id-from-namespace-annotation",
321 },
322 },
323 },
324 },
325 {
326 name: "default from namespace name if no namespace annotation specified and resource annotations are not supported",
327 resource: &k8s.Resource{
328 ObjectMeta: v1.ObjectMeta{
329 Annotations: map[string]string{
330 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
331 },
332 },
333 Spec: map[string]interface{}{
334 "field": "val",
335 },
336 },
337 ns: &corev1.Namespace{
338 ObjectMeta: v1.ObjectMeta{
339 Name: "namespace-name",
340 },
341 },
342 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
343 {
344 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
345 Key: "projectRef",
346 },
347 },
348 expectedResource: &k8s.Resource{
349 ObjectMeta: v1.ObjectMeta{
350 Annotations: map[string]string{
351 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
352 },
353 },
354 Spec: map[string]interface{}{
355 "field": "val",
356 "projectRef": map[string]interface{}{
357 "external": "namespace-name",
358 },
359 },
360 },
361 },
362 {
363 name: "default from namespace name if no namespace or resource annotations specified",
364 resource: &k8s.Resource{
365 Spec: map[string]interface{}{
366 "field": "val",
367 },
368 },
369 ns: &corev1.Namespace{
370 ObjectMeta: v1.ObjectMeta{
371 Name: "namespace-name",
372 },
373 },
374 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
375 {
376 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
377 Key: "projectRef",
378 },
379 },
380 expectedResource: &k8s.Resource{
381 Spec: map[string]interface{}{
382 "field": "val",
383 "projectRef": map[string]interface{}{
384 "external": "namespace-name",
385 },
386 },
387 },
388 },
389 {
390 name: "error if no namespace or resource annotations specified and resource does not support project references",
391 resource: &k8s.Resource{
392 Spec: map[string]interface{}{
393 "field": "val",
394 },
395 },
396 ns: &corev1.Namespace{
397 ObjectMeta: v1.ObjectMeta{
398 Name: "namespace-name",
399 },
400 },
401 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
402 {
403 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
404 Key: "folderRef",
405 },
406 },
407 shouldErr: true,
408 },
409 {
410 name: "only default supported reference from resource annotation",
411 resource: &k8s.Resource{
412 ObjectMeta: v1.ObjectMeta{
413 Annotations: map[string]string{
414 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
415 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
416 k8s.OrgIDAnnotation: "org-id-from-resource-annotation",
417 },
418 },
419 Spec: map[string]interface{}{
420 "field": "val",
421 },
422 },
423 ns: &corev1.Namespace{
424 ObjectMeta: v1.ObjectMeta{
425 Annotations: map[string]string{
426 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
427 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
428 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
429 },
430 Name: "namespace-name",
431 },
432 },
433 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
434 {
435 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
436 Key: "folderRef",
437 },
438 },
439 containers: []corekccv1alpha1.Container{
440 {Type: corekccv1alpha1.ContainerTypeFolder},
441 },
442 expectedResource: &k8s.Resource{
443 ObjectMeta: v1.ObjectMeta{
444 Annotations: map[string]string{
445 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
446 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
447 k8s.OrgIDAnnotation: "org-id-from-resource-annotation",
448 },
449 },
450 Spec: map[string]interface{}{
451 "field": "val",
452 "folderRef": map[string]interface{}{
453 "external": "folder-id-from-resource-annotation",
454 },
455 },
456 },
457 },
458 {
459 name: "only default supported reference from namespace annotation (no resource annotations specified)",
460 resource: &k8s.Resource{
461 Spec: map[string]interface{}{
462 "field": "val",
463 },
464 },
465 ns: &corev1.Namespace{
466 ObjectMeta: v1.ObjectMeta{
467 Annotations: map[string]string{
468 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
469 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
470 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
471 },
472 Name: "namespace-name",
473 },
474 },
475 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
476 {
477 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
478 Key: "folderRef",
479 },
480 },
481 containers: []corekccv1alpha1.Container{
482 {Type: corekccv1alpha1.ContainerTypeFolder},
483 },
484 expectedResource: &k8s.Resource{
485 Spec: map[string]interface{}{
486 "field": "val",
487 "folderRef": map[string]interface{}{
488 "external": "folder-id-from-namespace-annotation",
489 },
490 },
491 },
492 },
493 {
494 name: "only default supported reference from namespace annotation (no resource annotations supported)",
495 resource: &k8s.Resource{
496 Spec: map[string]interface{}{
497 "field": "val",
498 },
499 },
500 ns: &corev1.Namespace{
501 ObjectMeta: v1.ObjectMeta{
502 Annotations: map[string]string{
503 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
504 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
505 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
506 },
507 Name: "namespace-name",
508 },
509 },
510 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
511 {
512 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
513 Key: "folderRef",
514 },
515 },
516 expectedResource: &k8s.Resource{
517 Spec: map[string]interface{}{
518 "field": "val",
519 "folderRef": map[string]interface{}{
520 "external": "folder-id-from-namespace-annotation",
521 },
522 },
523 },
524 },
525 {
526 name: "error if multiple references supported and multiple resource annotations specified",
527 resource: &k8s.Resource{
528 ObjectMeta: v1.ObjectMeta{
529 Annotations: map[string]string{
530 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
531 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
532 },
533 },
534 Spec: map[string]interface{}{
535 "field": "val",
536 },
537 },
538 ns: &corev1.Namespace{
539 ObjectMeta: v1.ObjectMeta{
540 Annotations: map[string]string{
541 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
542 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
543 },
544 Name: "namespace-name",
545 },
546 },
547 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
548 {
549 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
550 Key: "projectRef",
551 },
552 {
553 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
554 Key: "folderRef",
555 },
556 },
557 containers: []corekccv1alpha1.Container{
558 {Type: corekccv1alpha1.ContainerTypeProject},
559 {Type: corekccv1alpha1.ContainerTypeFolder},
560 },
561 shouldErr: true,
562 },
563 {
564 name: "error if multiple references supported and multiple resource annotations specified with one being set to empty string",
565 resource: &k8s.Resource{
566 ObjectMeta: v1.ObjectMeta{
567 Annotations: map[string]string{
568 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
569 k8s.FolderIDAnnotation: "",
570 },
571 },
572 Spec: map[string]interface{}{
573 "field": "val",
574 },
575 },
576 ns: &corev1.Namespace{
577 ObjectMeta: v1.ObjectMeta{
578 Annotations: map[string]string{
579 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
580 k8s.FolderIDAnnotation: "",
581 },
582 Name: "namespace-name",
583 },
584 },
585 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
586 {
587 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
588 Key: "projectRef",
589 },
590 {
591 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
592 Key: "folderRef",
593 },
594 },
595 containers: []corekccv1alpha1.Container{
596 {Type: corekccv1alpha1.ContainerTypeProject},
597 {Type: corekccv1alpha1.ContainerTypeFolder},
598 },
599 shouldErr: true,
600 },
601 {
602 name: "error if multiple references supported and multiple namespace annotations specified (no resource annotations specified)",
603 resource: &k8s.Resource{
604 Spec: map[string]interface{}{
605 "field": "val",
606 },
607 },
608 ns: &corev1.Namespace{
609 ObjectMeta: v1.ObjectMeta{
610 Annotations: map[string]string{
611 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
612 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
613 },
614 Name: "namespace-name",
615 },
616 },
617 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
618 {
619 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
620 Key: "projectRef",
621 },
622 {
623 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
624 Key: "folderRef",
625 },
626 },
627 containers: []corekccv1alpha1.Container{
628 {Type: corekccv1alpha1.ContainerTypeProject},
629 {Type: corekccv1alpha1.ContainerTypeFolder},
630 },
631 shouldErr: true,
632 },
633 {
634 name: "error if multiple references supported and multiple namespace annotations specified with one being set to empty string (no resource annotations specified)",
635 resource: &k8s.Resource{
636 Spec: map[string]interface{}{
637 "field": "val",
638 },
639 },
640 ns: &corev1.Namespace{
641 ObjectMeta: v1.ObjectMeta{
642 Annotations: map[string]string{
643 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
644 k8s.FolderIDAnnotation: "",
645 },
646 Name: "namespace-name",
647 },
648 },
649 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
650 {
651 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
652 Key: "projectRef",
653 },
654 {
655 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
656 Key: "folderRef",
657 },
658 },
659 containers: []corekccv1alpha1.Container{
660 {Type: corekccv1alpha1.ContainerTypeProject},
661 {Type: corekccv1alpha1.ContainerTypeFolder},
662 },
663 shouldErr: true,
664 },
665 {
666 name: "error if multiple references supported and multiple namespace annotations specified (no resource annotations supported)",
667 resource: &k8s.Resource{
668 Spec: map[string]interface{}{
669 "field": "val",
670 },
671 },
672 ns: &corev1.Namespace{
673 ObjectMeta: v1.ObjectMeta{
674 Annotations: map[string]string{
675 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
676 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
677 },
678 Name: "namespace-name",
679 },
680 },
681 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
682 {
683 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
684 Key: "projectRef",
685 },
686 {
687 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
688 Key: "folderRef",
689 },
690 },
691 shouldErr: true,
692 },
693 {
694 name: "error if multiple references supported and multiple namespace annotations specified with one being set to empty string (no resource annotations supported)",
695 resource: &k8s.Resource{
696 Spec: map[string]interface{}{
697 "field": "val",
698 },
699 },
700 ns: &corev1.Namespace{
701 ObjectMeta: v1.ObjectMeta{
702 Annotations: map[string]string{
703 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
704 k8s.FolderIDAnnotation: "",
705 },
706 Name: "namespace-name",
707 },
708 },
709 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
710 {
711 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
712 Key: "projectRef",
713 },
714 {
715 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
716 Key: "folderRef",
717 },
718 },
719 shouldErr: true,
720 },
721 {
722 name: "defaulting creates a new spec map if none present",
723 resource: &k8s.Resource{
724 ObjectMeta: v1.ObjectMeta{
725 Annotations: map[string]string{
726 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
727 },
728 },
729 },
730 ns: &corev1.Namespace{
731 ObjectMeta: v1.ObjectMeta{
732 Annotations: map[string]string{
733 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
734 },
735 Name: "namespace-name",
736 },
737 },
738 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
739 {
740 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
741 Key: "projectRef",
742 },
743 },
744 containers: []corekccv1alpha1.Container{
745 {Type: corekccv1alpha1.ContainerTypeProject},
746 },
747 expectedResource: &k8s.Resource{
748 ObjectMeta: v1.ObjectMeta{
749 Annotations: map[string]string{
750 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
751 },
752 },
753 Spec: map[string]interface{}{
754 "projectRef": map[string]interface{}{
755 "external": "project-id-from-resource-annotation",
756 },
757 },
758 },
759 },
760 }
761
762 for _, tc := range tests {
763 tc := tc
764 t.Run(tc.name, func(t *testing.T) {
765 t.Parallel()
766 err := k8s.SetDefaultHierarchicalReference(tc.resource, tc.ns, tc.hierarchicalRefs, tc.containers)
767 if tc.shouldErr && err == nil {
768 t.Fatalf("got no error, but wanted one")
769 } else if !tc.shouldErr && err != nil {
770 t.Fatalf("got unexpected error: %v", err)
771 }
772
773 if err == nil {
774 if !test.Equals(t, tc.expectedResource, tc.resource) {
775 diff := cmp.Diff(tc.expectedResource, tc.resource)
776 t.Fatalf("unexpected diff in resource (-want +got):\n%v", diff)
777 }
778 }
779 })
780 }
781 }
782
783 func TestGetHierarchicalReference(t *testing.T) {
784 tests := []struct {
785 name string
786 resource *k8s.Resource
787 hierarchicalRefs []corekccv1alpha1.HierarchicalReference
788
789 resourceRef *corekccv1alpha1.ResourceReference
790 hierarchicalRef corekccv1alpha1.HierarchicalReference
791 shouldErr bool
792 }{
793 {
794 name: "return nil resource reference if resource doesn't have a spec",
795 resource: &k8s.Resource{},
796 },
797 {
798 name: "return nil resource reference if resource doesn't support hierarchical references",
799 resource: &k8s.Resource{
800 Spec: map[string]interface{}{
801 "folderRef": map[string]interface{}{
802 "name": "folder-dep",
803 },
804 },
805 },
806 },
807 {
808 name: "return nil resource reference if resource supports a hierarchical reference, but spec does not specify that reference",
809 resource: &k8s.Resource{
810 Spec: map[string]interface{}{
811 "field": "val",
812 },
813 },
814 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
815 {
816 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
817 Key: "folderRef",
818 },
819 },
820 },
821 {
822 name: "return resource reference if resource supports a hierarchical reference, and spec specifies that reference",
823 resource: &k8s.Resource{
824 Spec: map[string]interface{}{
825 "folderRef": map[string]interface{}{
826 "name": "folder-dep",
827 },
828 },
829 },
830 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
831 {
832 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
833 Key: "folderRef",
834 },
835 },
836 resourceRef: &corekccv1alpha1.ResourceReference{
837 Name: "folder-dep",
838 },
839 hierarchicalRef: corekccv1alpha1.HierarchicalReference{
840 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
841 Key: "folderRef",
842 },
843 },
844 {
845 name: "return nil resource reference if resource supports multiple hierarchical references, and spec specifies none of those references",
846 resource: &k8s.Resource{
847 Spec: map[string]interface{}{
848 "field": "val",
849 },
850 },
851 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
852 {
853 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
854 Key: "folderRef",
855 },
856 {
857 Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
858 Key: "organizationRef",
859 },
860 },
861 },
862 {
863 name: "return resource reference if resource supports multiple hierarchical references, and spec specifies one of those references",
864 resource: &k8s.Resource{
865 Spec: map[string]interface{}{
866 "folderRef": map[string]interface{}{
867 "name": "folder-dep",
868 },
869 },
870 },
871 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
872 {
873 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
874 Key: "folderRef",
875 },
876 {
877 Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
878 Key: "organizationRef",
879 },
880 },
881 resourceRef: &corekccv1alpha1.ResourceReference{
882 Name: "folder-dep",
883 },
884 hierarchicalRef: corekccv1alpha1.HierarchicalReference{
885 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
886 Key: "folderRef",
887 },
888 },
889 {
890 name: "error if resource supports multiple hierarchical references, but spec specifies more than one of those references",
891 resource: &k8s.Resource{
892 Spec: map[string]interface{}{
893 "folderRef": map[string]interface{}{
894 "name": "folder-dep",
895 },
896 "organizationRef": map[string]interface{}{
897 "external": "123456789",
898 },
899 },
900 },
901 hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
902 {
903 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
904 Key: "folderRef",
905 },
906 {
907 Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
908 Key: "organizationRef",
909 },
910 },
911 shouldErr: true,
912 },
913 }
914
915 for _, tc := range tests {
916 tc := tc
917 t.Run(tc.name, func(t *testing.T) {
918 t.Parallel()
919 resourceRef, hierarchicalRef, err := k8s.GetHierarchicalReference(tc.resource, tc.hierarchicalRefs)
920 if tc.shouldErr {
921 if err == nil {
922 t.Fatalf("got no error, but wanted one")
923 }
924 if resourceRef != nil {
925 t.Fatalf("got a non-nil resource reference, but wanted nil since function returned an error")
926 }
927 if !isEmpty(hierarchicalRef) {
928 t.Fatalf("got a non-empty hierarchical reference, but wanted empty since function returned an error")
929 }
930 return
931 }
932 if err != nil {
933 t.Fatalf("got unexpected error: %v", err)
934 }
935
936 if !test.Equals(t, tc.resourceRef, resourceRef) {
937 diff := cmp.Diff(tc.resourceRef, resourceRef)
938 t.Fatalf("unexpected diff in resulting resource reference (-want +got):\n%v", diff)
939 }
940 if !test.Equals(t, tc.hierarchicalRef, hierarchicalRef) {
941 diff := cmp.Diff(tc.hierarchicalRef, hierarchicalRef)
942 t.Fatalf("unexpected diff in resulting hierarchical reference (-want +got):\n%v", diff)
943 }
944 })
945 }
946 }
947
948 func TestSetHierarchicalReference(t *testing.T) {
949 tests := []struct {
950 name string
951 resource *k8s.Resource
952 hierarchicalRef *corekccv1alpha1.HierarchicalReference
953 val string
954
955 expectedResource *k8s.Resource
956 }{
957 {
958 name: "add hierarchical reference if not present",
959 resource: &k8s.Resource{
960 Spec: map[string]interface{}{
961 "field": "val",
962 },
963 },
964 hierarchicalRef: &corekccv1alpha1.HierarchicalReference{
965 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
966 Key: "projectRef",
967 },
968 val: "project-id",
969 expectedResource: &k8s.Resource{
970 Spec: map[string]interface{}{
971 "field": "val",
972 "projectRef": map[string]interface{}{
973 "external": "project-id",
974 },
975 },
976 },
977 },
978 {
979 name: "overwrite hierarchical reference if present",
980 resource: &k8s.Resource{
981 Spec: map[string]interface{}{
982 "projectRef": map[string]interface{}{
983 "external": "old-project-id",
984 },
985 },
986 },
987 hierarchicalRef: &corekccv1alpha1.HierarchicalReference{
988 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
989 Key: "projectRef",
990 },
991 val: "project-id",
992 expectedResource: &k8s.Resource{
993 Spec: map[string]interface{}{
994 "projectRef": map[string]interface{}{
995 "external": "project-id",
996 },
997 },
998 },
999 },
1000 {
1001 name: "add hierarchical reference even if spec not present",
1002 resource: &k8s.Resource{},
1003 hierarchicalRef: &corekccv1alpha1.HierarchicalReference{
1004 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
1005 Key: "projectRef",
1006 },
1007 val: "project-id",
1008 expectedResource: &k8s.Resource{
1009 Spec: map[string]interface{}{
1010 "projectRef": map[string]interface{}{
1011 "external": "project-id",
1012 },
1013 },
1014 },
1015 },
1016 }
1017
1018 for _, tc := range tests {
1019 tc := tc
1020 t.Run(tc.name, func(t *testing.T) {
1021 t.Parallel()
1022 if err := k8s.SetHierarchicalReference(tc.resource, tc.hierarchicalRef, tc.val); err != nil {
1023 t.Fatalf("got unexpected error: %v", err)
1024 }
1025 if !test.Equals(t, tc.expectedResource, tc.resource) {
1026 diff := cmp.Diff(tc.expectedResource, tc.resource)
1027 t.Fatalf("unexpected diff in resource (-want +got):\n%v", diff)
1028 }
1029 })
1030 }
1031 }
1032
1033 func isEmpty(h corekccv1alpha1.HierarchicalReference) bool {
1034 empty := corekccv1alpha1.HierarchicalReference{}
1035 return reflect.DeepEqual(h, empty)
1036 }
1037
View as plain text