1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package webhook
16
17 import (
18 "testing"
19
20 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
22 testutil "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
23 testdclschemaloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/dclschemaloader"
24 testservicemappingloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemappingloader"
25 testservicemetadataloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemetadataloader"
26
27 "github.com/google/go-cmp/cmp"
28 "github.com/nasa9084/go-openapi"
29 corev1 "k8s.io/api/core/v1"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32 )
33
34 func TestHandleContainerAnnotationsForDCLBasedResources(t *testing.T) {
35 tests := []struct {
36 name string
37 obj *unstructured.Unstructured
38 newObj *unstructured.Unstructured
39 ns *corev1.Namespace
40 schema *openapi.Schema
41 denied bool
42 }{
43 {
44 name: "no defaulting if resource supports neither containers nor hierarchical references",
45 obj: &unstructured.Unstructured{
46 Object: map[string]interface{}{
47 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
48 "kind": "Test6NoContainerOrHierarchicalRef",
49 "metadata": map[string]interface{}{
50 "name": "resource-name",
51 },
52 },
53 },
54 newObj: &unstructured.Unstructured{
55 Object: map[string]interface{}{
56 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
57 "kind": "Test6NoContainerOrHierarchicalRef",
58 "metadata": map[string]interface{}{
59 "name": "resource-name",
60 },
61 },
62 },
63 ns: &corev1.Namespace{
64 ObjectMeta: metav1.ObjectMeta{
65 Annotations: map[string]string{
66 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
67 },
68 Name: "namespace-name",
69 },
70 },
71 schema: &openapi.Schema{
72 Type: "object",
73 },
74 },
75
76
77
78
79
80 {
81 name: "no defaulting if resource already has annotation (project-scoped resource which only supports container annotations)",
82 obj: &unstructured.Unstructured{
83 Object: map[string]interface{}{
84 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
85 "kind": "Test6OnlyContainer",
86 "metadata": map[string]interface{}{
87 "annotations": map[string]interface{}{
88 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
89 },
90 "name": "resource-name",
91 },
92 },
93 },
94 newObj: &unstructured.Unstructured{
95 Object: map[string]interface{}{
96 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
97 "kind": "Test6OnlyContainer",
98 "metadata": map[string]interface{}{
99 "annotations": map[string]interface{}{
100 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
101 },
102 "name": "resource-name",
103 },
104 },
105 },
106 ns: &corev1.Namespace{
107 ObjectMeta: metav1.ObjectMeta{
108 Annotations: map[string]string{
109 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
110 },
111 Name: "namespace-name",
112 },
113 },
114 schema: &openapi.Schema{
115 Type: "object",
116 Extension: map[string]interface{}{
117 "x-dcl-parent-container": "project",
118 },
119 },
120 },
121 {
122 name: "default resource-level annotation from namespace annotation (project-scoped resource which only supports container annotations)",
123 obj: &unstructured.Unstructured{
124 Object: map[string]interface{}{
125 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
126 "kind": "Test6OnlyContainer",
127 "metadata": map[string]interface{}{
128 "name": "resource-name",
129 },
130 },
131 },
132 newObj: &unstructured.Unstructured{
133 Object: map[string]interface{}{
134 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
135 "kind": "Test6OnlyContainer",
136 "metadata": map[string]interface{}{
137 "annotations": map[string]interface{}{
138 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
139 },
140 "name": "resource-name",
141 },
142 },
143 },
144 ns: &corev1.Namespace{
145 ObjectMeta: metav1.ObjectMeta{
146 Annotations: map[string]string{
147 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
148 },
149 Name: "namespace-name",
150 },
151 },
152 schema: &openapi.Schema{
153 Type: "object",
154 Extension: map[string]interface{}{
155 "x-dcl-parent-container": "project",
156 },
157 },
158 },
159 {
160 name: "default resource-level annotation from namespace annotation (folder-scoped resource which only supports container annotations)",
161 obj: &unstructured.Unstructured{
162 Object: map[string]interface{}{
163 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
164 "kind": "Test6OnlyContainer",
165 "metadata": map[string]interface{}{
166 "name": "resource-name",
167 },
168 },
169 },
170 newObj: &unstructured.Unstructured{
171 Object: map[string]interface{}{
172 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
173 "kind": "Test6OnlyContainer",
174 "metadata": map[string]interface{}{
175 "annotations": map[string]interface{}{
176 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
177 },
178 "name": "resource-name",
179 },
180 },
181 },
182 ns: &corev1.Namespace{
183 ObjectMeta: metav1.ObjectMeta{
184 Annotations: map[string]string{
185 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
186 },
187 Name: "namespace-name",
188 },
189 },
190 schema: &openapi.Schema{
191 Type: "object",
192 Extension: map[string]interface{}{
193 "x-dcl-parent-container": "folder",
194 },
195 },
196 },
197 {
198 name: "default resource-level annotation from namespace annotation (organization-scoped resource which only supports container annotations)",
199 obj: &unstructured.Unstructured{
200 Object: map[string]interface{}{
201 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
202 "kind": "Test6OnlyContainer",
203 "metadata": map[string]interface{}{
204 "name": "resource-name",
205 },
206 },
207 },
208 newObj: &unstructured.Unstructured{
209 Object: map[string]interface{}{
210 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
211 "kind": "Test6OnlyContainer",
212 "metadata": map[string]interface{}{
213 "annotations": map[string]interface{}{
214 k8s.OrgIDAnnotation: "organization-id-from-namespace-annotation",
215 },
216 "name": "resource-name",
217 },
218 },
219 },
220 ns: &corev1.Namespace{
221 ObjectMeta: metav1.ObjectMeta{
222 Annotations: map[string]string{
223 k8s.OrgIDAnnotation: "organization-id-from-namespace-annotation",
224 },
225 Name: "namespace-name",
226 },
227 },
228 schema: &openapi.Schema{
229 Type: "object",
230 Extension: map[string]interface{}{
231 "x-dcl-parent-container": "organization",
232 },
233 },
234 },
235 {
236 name: "default resource-level annotation from namespace name if namespace has no annotation (project-scoped resource which only supports container annotations)",
237 obj: &unstructured.Unstructured{
238 Object: map[string]interface{}{
239 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
240 "kind": "Test6OnlyContainer",
241 "metadata": map[string]interface{}{
242 "name": "resource-name",
243 },
244 },
245 },
246 newObj: &unstructured.Unstructured{
247 Object: map[string]interface{}{
248 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
249 "kind": "Test6OnlyContainer",
250 "metadata": map[string]interface{}{
251 "annotations": map[string]interface{}{
252 k8s.ProjectIDAnnotation: "namespace-name",
253 },
254 "name": "resource-name",
255 },
256 },
257 },
258 ns: &corev1.Namespace{
259 ObjectMeta: metav1.ObjectMeta{
260 Name: "namespace-name",
261 },
262 },
263 schema: &openapi.Schema{
264 Type: "object",
265 Extension: map[string]interface{}{
266 "x-dcl-parent-container": "project",
267 },
268 },
269 },
270 {
271 name: "deny resource if no namespace annotation (non-project-scoped resource which only supports container annotations)",
272 obj: &unstructured.Unstructured{
273 Object: map[string]interface{}{
274 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
275 "kind": "Test6OnlyContainer",
276 "metadata": map[string]interface{}{
277 "name": "resource-name",
278 },
279 },
280 },
281 ns: &corev1.Namespace{
282 ObjectMeta: metav1.ObjectMeta{
283 Name: "namespace-name",
284 },
285 },
286 schema: &openapi.Schema{
287 Type: "object",
288 Extension: map[string]interface{}{
289 "x-dcl-parent-container": "folder",
290 },
291 },
292 denied: true,
293 },
294
295
296
297 {
298 name: "no defaulting if resource already has reference (project-scoped resource which supports both container annotations and hierarchical references)",
299 obj: &unstructured.Unstructured{
300 Object: map[string]interface{}{
301 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
302 "kind": "Test6BothContainerAndHierarchicalRef",
303 "metadata": map[string]interface{}{
304 "annotations": map[string]interface{}{
305 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
306 },
307 "name": "resource-name",
308 },
309 "spec": map[string]interface{}{
310 "projectRef": map[string]interface{}{
311 "name": "project-id-from-spec",
312 },
313 },
314 },
315 },
316 newObj: &unstructured.Unstructured{
317 Object: map[string]interface{}{
318 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
319 "kind": "Test6BothContainerAndHierarchicalRef",
320 "metadata": map[string]interface{}{
321 "annotations": map[string]interface{}{
322 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
323 },
324 "name": "resource-name",
325 },
326 "spec": map[string]interface{}{
327 "projectRef": map[string]interface{}{
328 "name": "project-id-from-spec",
329 },
330 },
331 },
332 },
333 schema: &openapi.Schema{
334 Type: "object",
335 Properties: map[string]*openapi.Schema{
336 "project": &openapi.Schema{
337 Type: "string",
338 Extension: map[string]interface{}{
339 "x-dcl-references": []interface{}{
340 map[interface{}]interface{}{
341 "field": "name",
342 "parent": true,
343 "resource": "Cloudresourcemanager/Project",
344 },
345 },
346 },
347 },
348 },
349 Extension: map[string]interface{}{
350 "x-dcl-parent-container": "project",
351 },
352 },
353 ns: &corev1.Namespace{
354 ObjectMeta: metav1.ObjectMeta{
355 Annotations: map[string]string{
356 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
357 },
358 Name: "namespace-name",
359 },
360 },
361 },
362 {
363 name: "default reference from resource annotation (project-scoped resource which supports both container annotations and hierarchical references)",
364 obj: &unstructured.Unstructured{
365 Object: map[string]interface{}{
366 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
367 "kind": "Test6BothContainerAndHierarchicalRef",
368 "metadata": map[string]interface{}{
369 "annotations": map[string]interface{}{
370 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
371 },
372 "name": "resource-name",
373 },
374 },
375 },
376 newObj: &unstructured.Unstructured{
377 Object: map[string]interface{}{
378 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
379 "kind": "Test6BothContainerAndHierarchicalRef",
380 "metadata": map[string]interface{}{
381 "annotations": map[string]interface{}{
382 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
383 },
384 "name": "resource-name",
385 },
386 "spec": map[string]interface{}{
387 "projectRef": map[string]interface{}{
388 "external": "project-id-from-resource-annotation",
389 },
390 },
391 },
392 },
393 schema: &openapi.Schema{
394 Type: "object",
395 Properties: map[string]*openapi.Schema{
396 "project": &openapi.Schema{
397 Type: "string",
398 Extension: map[string]interface{}{
399 "x-dcl-references": []interface{}{
400 map[interface{}]interface{}{
401 "field": "name",
402 "parent": true,
403 "resource": "Cloudresourcemanager/Project",
404 },
405 },
406 },
407 },
408 },
409 Extension: map[string]interface{}{
410 "x-dcl-parent-container": "project",
411 },
412 },
413 ns: &corev1.Namespace{
414 ObjectMeta: metav1.ObjectMeta{
415 Annotations: map[string]string{
416 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
417 },
418 Name: "namespace-name",
419 },
420 },
421 },
422 {
423 name: "default reference from namespace annotation (project-scoped resource which supports both container annotations and hierarchical references)",
424 obj: &unstructured.Unstructured{
425 Object: map[string]interface{}{
426 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
427 "kind": "Test6BothContainerAndHierarchicalRef",
428 "metadata": map[string]interface{}{
429 "name": "resource-name",
430 },
431 },
432 },
433 newObj: &unstructured.Unstructured{
434 Object: map[string]interface{}{
435 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
436 "kind": "Test6BothContainerAndHierarchicalRef",
437 "metadata": map[string]interface{}{
438 "name": "resource-name",
439 },
440 "spec": map[string]interface{}{
441 "projectRef": map[string]interface{}{
442 "external": "project-id-from-namespace-annotation",
443 },
444 },
445 },
446 },
447 schema: &openapi.Schema{
448 Type: "object",
449 Properties: map[string]*openapi.Schema{
450 "project": &openapi.Schema{
451 Type: "string",
452 Extension: map[string]interface{}{
453 "x-dcl-references": []interface{}{
454 map[interface{}]interface{}{
455 "field": "name",
456 "parent": true,
457 "resource": "Cloudresourcemanager/Project",
458 },
459 },
460 },
461 },
462 },
463 Extension: map[string]interface{}{
464 "x-dcl-parent-container": "project",
465 },
466 },
467 ns: &corev1.Namespace{
468 ObjectMeta: metav1.ObjectMeta{
469 Annotations: map[string]string{
470 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
471 },
472 Name: "namespace-name",
473 },
474 },
475 },
476 {
477 name: "default reference from namespace name (project-scoped resource which supports both container annotations and hierarchical references)",
478 obj: &unstructured.Unstructured{
479 Object: map[string]interface{}{
480 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
481 "kind": "Test6BothContainerAndHierarchicalRef",
482 "metadata": map[string]interface{}{
483 "name": "resource-name",
484 },
485 },
486 },
487 newObj: &unstructured.Unstructured{
488 Object: map[string]interface{}{
489 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
490 "kind": "Test6BothContainerAndHierarchicalRef",
491 "metadata": map[string]interface{}{
492 "name": "resource-name",
493 },
494 "spec": map[string]interface{}{
495 "projectRef": map[string]interface{}{
496 "external": "namespace-name",
497 },
498 },
499 },
500 },
501 schema: &openapi.Schema{
502 Type: "object",
503 Properties: map[string]*openapi.Schema{
504 "project": &openapi.Schema{
505 Type: "string",
506 Extension: map[string]interface{}{
507 "x-dcl-references": []interface{}{
508 map[interface{}]interface{}{
509 "field": "name",
510 "parent": true,
511 "resource": "Cloudresourcemanager/Project",
512 },
513 },
514 },
515 },
516 },
517 Extension: map[string]interface{}{
518 "x-dcl-parent-container": "project",
519 },
520 },
521 ns: &corev1.Namespace{
522 ObjectMeta: metav1.ObjectMeta{
523 Name: "namespace-name",
524 },
525 },
526 },
527 {
528 name: "default reference from resource annotation (project-scoped resource which supports both container annotations and hierarchical references, but doesn't have a x-dcl-parent-container extension)",
529 obj: &unstructured.Unstructured{
530 Object: map[string]interface{}{
531 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
532 "kind": "Test6BothContainerAndHierarchicalRef",
533 "metadata": map[string]interface{}{
534 "annotations": map[string]interface{}{
535 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
536 },
537 "name": "resource-name",
538 },
539 },
540 },
541 newObj: &unstructured.Unstructured{
542 Object: map[string]interface{}{
543 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
544 "kind": "Test6BothContainerAndHierarchicalRef",
545 "metadata": map[string]interface{}{
546 "annotations": map[string]interface{}{
547 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
548 },
549 "name": "resource-name",
550 },
551 "spec": map[string]interface{}{
552 "projectRef": map[string]interface{}{
553 "external": "project-id-from-resource-annotation",
554 },
555 },
556 },
557 },
558 schema: &openapi.Schema{
559 Type: "object",
560 Properties: map[string]*openapi.Schema{
561 "project": &openapi.Schema{
562 Type: "string",
563 Extension: map[string]interface{}{
564 "x-dcl-references": []interface{}{
565 map[interface{}]interface{}{
566 "field": "name",
567 "parent": true,
568 "resource": "Cloudresourcemanager/Project",
569 },
570 },
571 },
572 },
573 },
574 },
575 ns: &corev1.Namespace{
576 ObjectMeta: metav1.ObjectMeta{
577 Annotations: map[string]string{
578 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
579 },
580 Name: "namespace-name",
581 },
582 },
583 },
584
585
586
587 {
588 name: "no defaulting if resource already has reference (project-scoped resource which only supports hierarchical references)",
589 obj: &unstructured.Unstructured{
590 Object: map[string]interface{}{
591 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
592 "kind": "Test6OnlyHierarchicalRef",
593 "metadata": map[string]interface{}{
594 "annotations": map[string]interface{}{
595 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
596 },
597 "name": "resource-name",
598 },
599 "spec": map[string]interface{}{
600 "projectRef": map[string]interface{}{
601 "name": "project-id-from-spec",
602 },
603 },
604 },
605 },
606 newObj: &unstructured.Unstructured{
607 Object: map[string]interface{}{
608 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
609 "kind": "Test6OnlyHierarchicalRef",
610 "metadata": map[string]interface{}{
611 "annotations": map[string]interface{}{
612 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
613 },
614 "name": "resource-name",
615 },
616 "spec": map[string]interface{}{
617 "projectRef": map[string]interface{}{
618 "name": "project-id-from-spec",
619 },
620 },
621 },
622 },
623 schema: &openapi.Schema{
624 Type: "object",
625 Properties: map[string]*openapi.Schema{
626 "project": &openapi.Schema{
627 Type: "string",
628 Extension: map[string]interface{}{
629 "x-dcl-references": []interface{}{
630 map[interface{}]interface{}{
631 "field": "name",
632 "parent": true,
633 "resource": "Cloudresourcemanager/Project",
634 },
635 },
636 },
637 },
638 },
639 },
640 ns: &corev1.Namespace{
641 ObjectMeta: metav1.ObjectMeta{
642 Annotations: map[string]string{
643 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
644 },
645 Name: "namespace-name",
646 },
647 },
648 },
649 {
650 name: "default reference from namespace annotation (project-scoped resource which only supports hierarchical references)",
651 obj: &unstructured.Unstructured{
652 Object: map[string]interface{}{
653 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
654 "kind": "Test6OnlyHierarchicalRef",
655 "metadata": map[string]interface{}{
656 "annotations": map[string]interface{}{
657 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
658 },
659 "name": "resource-name",
660 },
661 },
662 },
663 newObj: &unstructured.Unstructured{
664 Object: map[string]interface{}{
665 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
666 "kind": "Test6OnlyHierarchicalRef",
667 "metadata": map[string]interface{}{
668 "annotations": map[string]interface{}{
669 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
670 },
671 "name": "resource-name",
672 },
673 "spec": map[string]interface{}{
674 "projectRef": map[string]interface{}{
675 "external": "project-id-from-namespace-annotation",
676 },
677 },
678 },
679 },
680 schema: &openapi.Schema{
681 Type: "object",
682 Properties: map[string]*openapi.Schema{
683 "project": &openapi.Schema{
684 Type: "string",
685 Extension: map[string]interface{}{
686 "x-dcl-references": []interface{}{
687 map[interface{}]interface{}{
688 "field": "name",
689 "parent": true,
690 "resource": "Cloudresourcemanager/Project",
691 },
692 },
693 },
694 },
695 },
696 },
697 ns: &corev1.Namespace{
698 ObjectMeta: metav1.ObjectMeta{
699 Annotations: map[string]string{
700 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
701 },
702 Name: "namespace-name",
703 },
704 },
705 },
706 {
707 name: "default reference from namespace name (project-scoped resource which only supports hierarchical references)",
708 obj: &unstructured.Unstructured{
709 Object: map[string]interface{}{
710 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
711 "kind": "Test6OnlyHierarchicalRef",
712 "metadata": map[string]interface{}{
713 "annotations": map[string]interface{}{
714 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
715 },
716 "name": "resource-name",
717 },
718 },
719 },
720 newObj: &unstructured.Unstructured{
721 Object: map[string]interface{}{
722 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
723 "kind": "Test6OnlyHierarchicalRef",
724 "metadata": map[string]interface{}{
725 "annotations": map[string]interface{}{
726 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
727 },
728 "name": "resource-name",
729 },
730 "spec": map[string]interface{}{
731 "projectRef": map[string]interface{}{
732 "external": "namespace-name",
733 },
734 },
735 },
736 },
737 schema: &openapi.Schema{
738 Type: "object",
739 Properties: map[string]*openapi.Schema{
740 "project": &openapi.Schema{
741 Type: "string",
742 Extension: map[string]interface{}{
743 "x-dcl-references": []interface{}{
744 map[interface{}]interface{}{
745 "field": "name",
746 "parent": true,
747 "resource": "Cloudresourcemanager/Project",
748 },
749 },
750 },
751 },
752 },
753 },
754 ns: &corev1.Namespace{
755 ObjectMeta: metav1.ObjectMeta{
756 Name: "namespace-name",
757 },
758 },
759 },
760 {
761 name: "default reference from namespace annotation (multi-parent resource which only supports hierarchical references)",
762 obj: &unstructured.Unstructured{
763 Object: map[string]interface{}{
764 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
765 "kind": "Test6OnlyHierarchicalRef",
766 "metadata": map[string]interface{}{
767 "annotations": map[string]interface{}{
768 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
769 },
770 "name": "resource-name",
771 },
772 },
773 },
774 newObj: &unstructured.Unstructured{
775 Object: map[string]interface{}{
776 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
777 "kind": "Test6OnlyHierarchicalRef",
778 "metadata": map[string]interface{}{
779 "annotations": map[string]interface{}{
780 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
781 },
782 "name": "resource-name",
783 },
784 "spec": map[string]interface{}{
785 "folderRef": map[string]interface{}{
786 "external": "folder-id-from-namespace-annotation",
787 },
788 },
789 },
790 },
791 schema: &openapi.Schema{
792 Type: "object",
793 Properties: map[string]*openapi.Schema{
794 "parent": &openapi.Schema{
795 Type: "string",
796 Extension: map[string]interface{}{
797 "x-dcl-references": []interface{}{
798 map[interface{}]interface{}{
799 "field": "name",
800 "parent": true,
801 "resource": "Cloudresourcemanager/Project",
802 },
803 map[interface{}]interface{}{
804 "field": "name",
805 "parent": true,
806 "resource": "Cloudresourcemanager/Folder",
807 },
808 map[interface{}]interface{}{
809 "field": "name",
810 "parent": true,
811 "resource": "Cloudresourcemanager/Organization",
812 },
813 },
814 },
815 },
816 },
817 },
818 ns: &corev1.Namespace{
819 ObjectMeta: metav1.ObjectMeta{
820 Annotations: map[string]string{
821 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
822 },
823 Name: "namespace-name",
824 },
825 },
826 },
827 {
828 name: "default reference from namespace name (multi-parent resource which can be project-scoped and which only supports hierarchical references)",
829 obj: &unstructured.Unstructured{
830 Object: map[string]interface{}{
831 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
832 "kind": "Test6OnlyHierarchicalRef",
833 "metadata": map[string]interface{}{
834 "annotations": map[string]interface{}{
835 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
836 },
837 "name": "resource-name",
838 },
839 },
840 },
841 newObj: &unstructured.Unstructured{
842 Object: map[string]interface{}{
843 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
844 "kind": "Test6OnlyHierarchicalRef",
845 "metadata": map[string]interface{}{
846 "annotations": map[string]interface{}{
847 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
848 },
849 "name": "resource-name",
850 },
851 "spec": map[string]interface{}{
852 "projectRef": map[string]interface{}{
853 "external": "namespace-name",
854 },
855 },
856 },
857 },
858 schema: &openapi.Schema{
859 Type: "object",
860 Properties: map[string]*openapi.Schema{
861 "parent": &openapi.Schema{
862 Type: "string",
863 Extension: map[string]interface{}{
864 "x-dcl-references": []interface{}{
865 map[interface{}]interface{}{
866 "field": "name",
867 "parent": true,
868 "resource": "Cloudresourcemanager/Project",
869 },
870 map[interface{}]interface{}{
871 "field": "name",
872 "parent": true,
873 "resource": "Cloudresourcemanager/Folder",
874 },
875 map[interface{}]interface{}{
876 "field": "name",
877 "parent": true,
878 "resource": "Cloudresourcemanager/Organization",
879 },
880 },
881 },
882 },
883 },
884 },
885 ns: &corev1.Namespace{
886 ObjectMeta: metav1.ObjectMeta{
887 Name: "namespace-name",
888 },
889 },
890 },
891 {
892 name: "deny resource if more than one namespace annotation (multi-parent resource which only supports hierarchical references)",
893 obj: &unstructured.Unstructured{
894 Object: map[string]interface{}{
895 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
896 "kind": "Test6OnlyHierarchicalRef",
897 "metadata": map[string]interface{}{
898 "name": "resource-name",
899 },
900 },
901 },
902 schema: &openapi.Schema{
903 Type: "object",
904 Properties: map[string]*openapi.Schema{
905 "parent": &openapi.Schema{
906 Type: "string",
907 Extension: map[string]interface{}{
908 "x-dcl-references": []interface{}{
909 map[interface{}]interface{}{
910 "field": "name",
911 "parent": true,
912 "resource": "Cloudresourcemanager/Project",
913 },
914 map[interface{}]interface{}{
915 "field": "name",
916 "parent": true,
917 "resource": "Cloudresourcemanager/Folder",
918 },
919 map[interface{}]interface{}{
920 "field": "name",
921 "parent": true,
922 "resource": "Cloudresourcemanager/Organization",
923 },
924 },
925 },
926 },
927 },
928 },
929 ns: &corev1.Namespace{
930 ObjectMeta: metav1.ObjectMeta{
931 Annotations: map[string]string{
932 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
933 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
934 },
935 Name: "namespace-name",
936 },
937 },
938 denied: true,
939 },
940 {
941 name: "deny resource if no namespace annotation (non-project-scoped resource which only supports hierarchical references)",
942 obj: &unstructured.Unstructured{
943 Object: map[string]interface{}{
944 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
945 "kind": "Test6OnlyHierarchicalRef",
946 "metadata": map[string]interface{}{
947 "name": "resource-name",
948 },
949 },
950 },
951 schema: &openapi.Schema{
952 Type: "object",
953 Properties: map[string]*openapi.Schema{
954 "parent": &openapi.Schema{
955 Type: "string",
956 Extension: map[string]interface{}{
957 "x-dcl-references": []interface{}{
958 map[interface{}]interface{}{
959 "field": "name",
960 "parent": true,
961 "resource": "Cloudresourcemanager/Folder",
962 },
963 map[interface{}]interface{}{
964 "field": "name",
965 "parent": true,
966 "resource": "Cloudresourcemanager/Organization",
967 },
968 },
969 },
970 },
971 },
972 },
973 ns: &corev1.Namespace{
974 ObjectMeta: metav1.ObjectMeta{
975 Name: "namespace-name",
976 },
977 },
978 denied: true,
979 },
980 {
981 name: "allow resource even if no annotations found on resource/namespace as long as resource has reference (multi-parent resource which only supports hierarchical references)",
982 obj: &unstructured.Unstructured{
983 Object: map[string]interface{}{
984 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
985 "kind": "Test6OnlyHierarchicalRef",
986 "metadata": map[string]interface{}{
987 "name": "resource-name",
988 },
989 "spec": map[string]interface{}{
990 "folderRef": map[string]interface{}{
991 "name": "folder-id-from-spec",
992 },
993 },
994 },
995 },
996 newObj: &unstructured.Unstructured{
997 Object: map[string]interface{}{
998 "apiVersion": "test6.cnrm.cloud.google.com/v1alpha1",
999 "kind": "Test6OnlyHierarchicalRef",
1000 "metadata": map[string]interface{}{
1001 "name": "resource-name",
1002 },
1003 "spec": map[string]interface{}{
1004 "folderRef": map[string]interface{}{
1005 "name": "folder-id-from-spec",
1006 },
1007 },
1008 },
1009 },
1010 schema: &openapi.Schema{
1011 Type: "object",
1012 Properties: map[string]*openapi.Schema{
1013 "parent": &openapi.Schema{
1014 Type: "string",
1015 Extension: map[string]interface{}{
1016 "x-dcl-references": []interface{}{
1017 map[interface{}]interface{}{
1018 "field": "name",
1019 "parent": true,
1020 "resource": "Cloudresourcemanager/Folder",
1021 },
1022 map[interface{}]interface{}{
1023 "field": "name",
1024 "parent": true,
1025 "resource": "Cloudresourcemanager/Organization",
1026 },
1027 },
1028 },
1029 },
1030 },
1031 },
1032 ns: &corev1.Namespace{
1033 ObjectMeta: metav1.ObjectMeta{
1034 Name: "namespace-name",
1035 },
1036 },
1037 },
1038 }
1039
1040 smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
1041 for _, tc := range tests {
1042 tc := tc
1043 t.Run(tc.name, func(t *testing.T) {
1044 t.Parallel()
1045 dclSchemaKey := testdclschemaloader.DCLSchemaKeyForGVK(t, tc.obj.GroupVersionKind(), smLoader)
1046 dclSchemaMap := map[string]*openapi.Schema{
1047 dclSchemaKey: tc.schema,
1048
1049
1050
1051
1052 "cloudresourcemanager_ga_project": &openapi.Schema{},
1053 "cloudresourcemanager_ga_folder": &openapi.Schema{},
1054 }
1055 dclSchemaLoader := testdclschemaloader.New(dclSchemaMap)
1056 response := handleContainerAnnotationsForDCLBasedResources(tc.obj, tc.ns, dclSchemaLoader, smLoader)
1057 if tc.denied {
1058 if response.Allowed {
1059 t.Fatalf("expected request to be denied, but was allowed. Response:\n%v", response)
1060 }
1061 return
1062 }
1063 if !response.Allowed {
1064 t.Fatalf("request was unexpectedly denied. Response:\n%v", response)
1065 }
1066 expectedResponse := constructPatchResponse(tc.obj, tc.newObj)
1067 if !testutil.Equals(t, expectedResponse, response) {
1068 diff := cmp.Diff(expectedResponse, response)
1069 t.Fatalf("unexpected diff in the response (-want +got):\n%v", diff)
1070 }
1071 })
1072 }
1073 }
1074
1075 func TestHandleContainerAnnotationsForTFBasedResources(t *testing.T) {
1076 tests := []struct {
1077 name string
1078 obj *unstructured.Unstructured
1079 ns *corev1.Namespace
1080
1081 newObj *unstructured.Unstructured
1082 denied bool
1083 }{
1084 {
1085 name: "no defaulting if resource supports neither containers nor hierarchical references",
1086 obj: &unstructured.Unstructured{
1087 Object: map[string]interface{}{
1088 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1089 "kind": "Test4NoParentResource",
1090 "metadata": map[string]interface{}{
1091 "name": "resource-name",
1092 },
1093 },
1094 },
1095 newObj: &unstructured.Unstructured{
1096 Object: map[string]interface{}{
1097 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1098 "kind": "Test4NoParentResource",
1099 "metadata": map[string]interface{}{
1100 "name": "resource-name",
1101 },
1102 },
1103 },
1104 ns: &corev1.Namespace{
1105 ObjectMeta: metav1.ObjectMeta{
1106 Annotations: map[string]string{
1107 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1108 },
1109 Name: "namespace-name",
1110 },
1111 },
1112 },
1113
1114
1115
1116
1117
1118 {
1119 name: "no defaulting if resource already has annotation (project-scoped resource which only supports container annotations)",
1120 obj: &unstructured.Unstructured{
1121 Object: map[string]interface{}{
1122 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1123 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1124 "metadata": map[string]interface{}{
1125 "annotations": map[string]interface{}{
1126 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1127 },
1128 "name": "resource-name",
1129 },
1130 },
1131 },
1132 newObj: &unstructured.Unstructured{
1133 Object: map[string]interface{}{
1134 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1135 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1136 "metadata": map[string]interface{}{
1137 "annotations": map[string]interface{}{
1138 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1139 },
1140 "name": "resource-name",
1141 },
1142 },
1143 },
1144 ns: &corev1.Namespace{
1145 ObjectMeta: metav1.ObjectMeta{
1146 Annotations: map[string]string{
1147 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1148 },
1149 Name: "namespace-name",
1150 },
1151 },
1152 },
1153 {
1154 name: "default resource-level annotation from namespace annotation (project-scoped resource which only supports container annotations)",
1155 obj: &unstructured.Unstructured{
1156 Object: map[string]interface{}{
1157 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1158 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1159 "metadata": map[string]interface{}{
1160 "name": "resource-name",
1161 },
1162 },
1163 },
1164 newObj: &unstructured.Unstructured{
1165 Object: map[string]interface{}{
1166 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1167 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1168 "metadata": map[string]interface{}{
1169 "annotations": map[string]interface{}{
1170 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1171 },
1172 "name": "resource-name",
1173 },
1174 },
1175 },
1176 ns: &corev1.Namespace{
1177 ObjectMeta: metav1.ObjectMeta{
1178 Annotations: map[string]string{
1179 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1180 },
1181 Name: "namespace-name",
1182 },
1183 },
1184 },
1185 {
1186 name: "default resource-level annotation from namespace name if namespace has no annotation (project-scoped resource which only supports container annotations)",
1187 obj: &unstructured.Unstructured{
1188 Object: map[string]interface{}{
1189 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1190 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1191 "metadata": map[string]interface{}{
1192 "name": "resource-name",
1193 },
1194 },
1195 },
1196 newObj: &unstructured.Unstructured{
1197 Object: map[string]interface{}{
1198 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1199 "kind": "Test4ProjectScopedResourceWithOnlyContainerSupport",
1200 "metadata": map[string]interface{}{
1201 "annotations": map[string]interface{}{
1202 k8s.ProjectIDAnnotation: "namespace-name",
1203 },
1204 "name": "resource-name",
1205 },
1206 },
1207 },
1208 ns: &corev1.Namespace{
1209 ObjectMeta: metav1.ObjectMeta{
1210 Name: "namespace-name",
1211 },
1212 },
1213 },
1214 {
1215 name: "no defaulting if resource already has annotation (multi-parent resource which only supports container annotations)",
1216 obj: &unstructured.Unstructured{
1217 Object: map[string]interface{}{
1218 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1219 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1220 "metadata": map[string]interface{}{
1221 "annotations": map[string]interface{}{
1222 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
1223 },
1224 "name": "resource-name",
1225 },
1226 },
1227 },
1228 newObj: &unstructured.Unstructured{
1229 Object: map[string]interface{}{
1230 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1231 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1232 "metadata": map[string]interface{}{
1233 "annotations": map[string]interface{}{
1234 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
1235 },
1236 "name": "resource-name",
1237 },
1238 },
1239 },
1240 ns: &corev1.Namespace{
1241 ObjectMeta: metav1.ObjectMeta{
1242 Annotations: map[string]string{
1243 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1244 },
1245 Name: "namespace-name",
1246 },
1247 },
1248 },
1249 {
1250 name: "default resource-level annotation from namespace annotation (multi-parent resource which only supports container annotations)",
1251 obj: &unstructured.Unstructured{
1252 Object: map[string]interface{}{
1253 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1254 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1255 "metadata": map[string]interface{}{
1256 "name": "resource-name",
1257 },
1258 },
1259 },
1260 newObj: &unstructured.Unstructured{
1261 Object: map[string]interface{}{
1262 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1263 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1264 "metadata": map[string]interface{}{
1265 "annotations": map[string]interface{}{
1266 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1267 },
1268 "name": "resource-name",
1269 },
1270 },
1271 },
1272 ns: &corev1.Namespace{
1273 ObjectMeta: metav1.ObjectMeta{
1274 Annotations: map[string]string{
1275 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1276 },
1277 Name: "namespace-name",
1278 },
1279 },
1280 },
1281 {
1282 name: "deny resource if more than one namespace annotation (multi-parent resource which only supports container annotations)",
1283 obj: &unstructured.Unstructured{
1284 Object: map[string]interface{}{
1285 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1286 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1287 "metadata": map[string]interface{}{
1288 "name": "resource-name",
1289 },
1290 },
1291 },
1292 ns: &corev1.Namespace{
1293 ObjectMeta: metav1.ObjectMeta{
1294 Annotations: map[string]string{
1295 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1296 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
1297 },
1298 Name: "namespace-name",
1299 },
1300 },
1301 denied: true,
1302 },
1303 {
1304 name: "deny resource if no namespace annotation (non-project-scoped resource which only supports container annotations)",
1305 obj: &unstructured.Unstructured{
1306 Object: map[string]interface{}{
1307 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1308 "kind": "Test4MultiParentResourceWithOnlyContainerSupport",
1309 "metadata": map[string]interface{}{
1310 "name": "resource-name",
1311 },
1312 },
1313 },
1314 ns: &corev1.Namespace{
1315 ObjectMeta: metav1.ObjectMeta{
1316 Name: "namespace-name",
1317 },
1318 },
1319 denied: true,
1320 },
1321
1322
1323
1324 {
1325 name: "no defaulting if resource already has reference (project-scoped resource which supports both container annotations and hierarchical references)",
1326 obj: &unstructured.Unstructured{
1327 Object: map[string]interface{}{
1328 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1329 "kind": "Test4ProjectScopedResource",
1330 "metadata": map[string]interface{}{
1331 "annotations": map[string]interface{}{
1332 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1333 },
1334 "name": "resource-name",
1335 },
1336 "spec": map[string]interface{}{
1337 "projectRef": map[string]interface{}{
1338 "name": "project-id-from-spec",
1339 },
1340 },
1341 },
1342 },
1343 newObj: &unstructured.Unstructured{
1344 Object: map[string]interface{}{
1345 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1346 "kind": "Test4ProjectScopedResource",
1347 "metadata": map[string]interface{}{
1348 "annotations": map[string]interface{}{
1349 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1350 },
1351 "name": "resource-name",
1352 },
1353 "spec": map[string]interface{}{
1354 "projectRef": map[string]interface{}{
1355 "name": "project-id-from-spec",
1356 },
1357 },
1358 },
1359 },
1360 ns: &corev1.Namespace{
1361 ObjectMeta: metav1.ObjectMeta{
1362 Annotations: map[string]string{
1363 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1364 },
1365 Name: "namespace-name",
1366 },
1367 },
1368 },
1369 {
1370 name: "default reference from resource annotation (project-scoped resource which supports both container annotations and hierarchical references)",
1371 obj: &unstructured.Unstructured{
1372 Object: map[string]interface{}{
1373 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1374 "kind": "Test4ProjectScopedResource",
1375 "metadata": map[string]interface{}{
1376 "annotations": map[string]interface{}{
1377 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1378 },
1379 "name": "resource-name",
1380 },
1381 },
1382 },
1383 newObj: &unstructured.Unstructured{
1384 Object: map[string]interface{}{
1385 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1386 "kind": "Test4ProjectScopedResource",
1387 "metadata": map[string]interface{}{
1388 "annotations": map[string]interface{}{
1389 k8s.ProjectIDAnnotation: "project-id-from-resource-annotation",
1390 },
1391 "name": "resource-name",
1392 },
1393 "spec": map[string]interface{}{
1394 "projectRef": map[string]interface{}{
1395 "external": "project-id-from-resource-annotation",
1396 },
1397 },
1398 },
1399 },
1400 ns: &corev1.Namespace{
1401 ObjectMeta: metav1.ObjectMeta{
1402 Annotations: map[string]string{
1403 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1404 },
1405 Name: "namespace-name",
1406 },
1407 },
1408 },
1409 {
1410 name: "default reference from namespace annotation (project-scoped resource which supports both container annotations and hierarchical references)",
1411 obj: &unstructured.Unstructured{
1412 Object: map[string]interface{}{
1413 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1414 "kind": "Test4ProjectScopedResource",
1415 "metadata": map[string]interface{}{
1416 "name": "resource-name",
1417 },
1418 },
1419 },
1420 newObj: &unstructured.Unstructured{
1421 Object: map[string]interface{}{
1422 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1423 "kind": "Test4ProjectScopedResource",
1424 "metadata": map[string]interface{}{
1425 "name": "resource-name",
1426 },
1427 "spec": map[string]interface{}{
1428 "projectRef": map[string]interface{}{
1429 "external": "project-id-from-namespace-annotation",
1430 },
1431 },
1432 },
1433 },
1434 ns: &corev1.Namespace{
1435 ObjectMeta: metav1.ObjectMeta{
1436 Annotations: map[string]string{
1437 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1438 },
1439 Name: "namespace-name",
1440 },
1441 },
1442 },
1443 {
1444 name: "default reference from namespace name (project-scoped resource which supports both container annotations and hierarchical references)",
1445 obj: &unstructured.Unstructured{
1446 Object: map[string]interface{}{
1447 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1448 "kind": "Test4ProjectScopedResource",
1449 "metadata": map[string]interface{}{
1450 "name": "resource-name",
1451 },
1452 },
1453 },
1454 newObj: &unstructured.Unstructured{
1455 Object: map[string]interface{}{
1456 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1457 "kind": "Test4ProjectScopedResource",
1458 "metadata": map[string]interface{}{
1459 "name": "resource-name",
1460 },
1461 "spec": map[string]interface{}{
1462 "projectRef": map[string]interface{}{
1463 "external": "namespace-name",
1464 },
1465 },
1466 },
1467 },
1468 ns: &corev1.Namespace{
1469 ObjectMeta: metav1.ObjectMeta{
1470 Name: "namespace-name",
1471 },
1472 },
1473 },
1474 {
1475 name: "default reference from resource annotation (multi-parent resource which supports both container annotations and hierarchical references)",
1476 obj: &unstructured.Unstructured{
1477 Object: map[string]interface{}{
1478 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1479 "kind": "Test4MultiParentResource",
1480 "metadata": map[string]interface{}{
1481 "annotations": map[string]interface{}{
1482 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
1483 },
1484 "name": "resource-name",
1485 },
1486 },
1487 },
1488 newObj: &unstructured.Unstructured{
1489 Object: map[string]interface{}{
1490 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1491 "kind": "Test4MultiParentResource",
1492 "metadata": map[string]interface{}{
1493 "annotations": map[string]interface{}{
1494 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
1495 },
1496 "name": "resource-name",
1497 },
1498 "spec": map[string]interface{}{
1499 "folderRef": map[string]interface{}{
1500 "external": "folder-id-from-resource-annotation",
1501 },
1502 },
1503 },
1504 },
1505 ns: &corev1.Namespace{
1506 ObjectMeta: metav1.ObjectMeta{
1507 Annotations: map[string]string{
1508 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1509 },
1510 Name: "namespace-name",
1511 },
1512 },
1513 },
1514 {
1515 name: "deny resource if more than one resource annotation (multi-parent resource which supports both container annotations and hierarchical references)",
1516 obj: &unstructured.Unstructured{
1517 Object: map[string]interface{}{
1518 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1519 "kind": "Test4MultiParentResource",
1520 "metadata": map[string]interface{}{
1521 "annotations": map[string]interface{}{
1522 k8s.FolderIDAnnotation: "folder-id-from-resource-annotation",
1523 k8s.OrgIDAnnotation: "org-id-from-resource-annotation",
1524 },
1525 "name": "resource-name",
1526 },
1527 },
1528 },
1529 ns: &corev1.Namespace{
1530 ObjectMeta: metav1.ObjectMeta{
1531 Annotations: map[string]string{
1532 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1533 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
1534 },
1535 Name: "namespace-name",
1536 },
1537 },
1538 denied: true,
1539 },
1540 {
1541 name: "default reference from namespace annotation (multi-parent resource which supports both container annotations and hierarchical references)",
1542 obj: &unstructured.Unstructured{
1543 Object: map[string]interface{}{
1544 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1545 "kind": "Test4MultiParentResource",
1546 "metadata": map[string]interface{}{
1547 "name": "resource-name",
1548 },
1549 },
1550 },
1551 newObj: &unstructured.Unstructured{
1552 Object: map[string]interface{}{
1553 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1554 "kind": "Test4MultiParentResource",
1555 "metadata": map[string]interface{}{
1556 "name": "resource-name",
1557 },
1558 "spec": map[string]interface{}{
1559 "folderRef": map[string]interface{}{
1560 "external": "folder-id-from-namespace-annotation",
1561 },
1562 },
1563 },
1564 },
1565 ns: &corev1.Namespace{
1566 ObjectMeta: metav1.ObjectMeta{
1567 Annotations: map[string]string{
1568 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1569 },
1570 Name: "namespace-name",
1571 },
1572 },
1573 },
1574 {
1575 name: "deny resource if more than one namespace annotation (multi-parent resource which supports both container annotations and hierarchical references)",
1576 obj: &unstructured.Unstructured{
1577 Object: map[string]interface{}{
1578 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1579 "kind": "Test4MultiParentResource",
1580 "metadata": map[string]interface{}{
1581 "name": "resource-name",
1582 },
1583 },
1584 },
1585 ns: &corev1.Namespace{
1586 ObjectMeta: metav1.ObjectMeta{
1587 Annotations: map[string]string{
1588 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1589 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
1590 },
1591 Name: "namespace-name",
1592 },
1593 },
1594 denied: true,
1595 },
1596 {
1597 name: "deny resource if no namespace annotation (non-project-scoped resource which supports both container annotations and hierarchical references)",
1598 obj: &unstructured.Unstructured{
1599 Object: map[string]interface{}{
1600 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1601 "kind": "Test4MultiParentResource",
1602 "metadata": map[string]interface{}{
1603 "name": "resource-name",
1604 },
1605 },
1606 },
1607 ns: &corev1.Namespace{
1608 ObjectMeta: metav1.ObjectMeta{
1609 Name: "namespace-name",
1610 },
1611 },
1612 denied: true,
1613 },
1614 {
1615 name: "allow resource even if no annotations found on resource/namespace as long as resource has reference (multi-parent resource which supports both container annotations and hierarchical references)",
1616 obj: &unstructured.Unstructured{
1617 Object: map[string]interface{}{
1618 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1619 "kind": "Test4MultiParentResource",
1620 "metadata": map[string]interface{}{
1621 "name": "resource-name",
1622 },
1623 "spec": map[string]interface{}{
1624 "folderRef": map[string]interface{}{
1625 "name": "project-id-from-spec",
1626 },
1627 },
1628 },
1629 },
1630 newObj: &unstructured.Unstructured{
1631 Object: map[string]interface{}{
1632 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1633 "kind": "Test4MultiParentResource",
1634 "metadata": map[string]interface{}{
1635 "name": "resource-name",
1636 },
1637 "spec": map[string]interface{}{
1638 "folderRef": map[string]interface{}{
1639 "name": "project-id-from-spec",
1640 },
1641 },
1642 },
1643 },
1644 ns: &corev1.Namespace{
1645 ObjectMeta: metav1.ObjectMeta{
1646 Name: "namespace-name",
1647 },
1648 },
1649 },
1650
1651
1652
1653 {
1654 name: "no defaulting if resource already has reference (project-scoped resource which only supports hierarchical references)",
1655 obj: &unstructured.Unstructured{
1656 Object: map[string]interface{}{
1657 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1658 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1659 "metadata": map[string]interface{}{
1660 "name": "resource-name",
1661 },
1662 "spec": map[string]interface{}{
1663 "projectRef": map[string]interface{}{
1664 "name": "project-id-from-spec",
1665 },
1666 },
1667 },
1668 },
1669 newObj: &unstructured.Unstructured{
1670 Object: map[string]interface{}{
1671 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1672 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1673 "metadata": map[string]interface{}{
1674 "name": "resource-name",
1675 },
1676 "spec": map[string]interface{}{
1677 "projectRef": map[string]interface{}{
1678 "name": "project-id-from-spec",
1679 },
1680 },
1681 },
1682 },
1683 ns: &corev1.Namespace{
1684 ObjectMeta: metav1.ObjectMeta{
1685 Annotations: map[string]string{
1686 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1687 },
1688 Name: "namespace-name",
1689 },
1690 },
1691 },
1692 {
1693 name: "default reference from namespace annotation (project-scoped resource which only supports hierarchical references)",
1694 obj: &unstructured.Unstructured{
1695 Object: map[string]interface{}{
1696 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1697 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1698 "metadata": map[string]interface{}{
1699 "name": "resource-name",
1700 },
1701 },
1702 },
1703 newObj: &unstructured.Unstructured{
1704 Object: map[string]interface{}{
1705 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1706 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1707 "metadata": map[string]interface{}{
1708 "name": "resource-name",
1709 },
1710 "spec": map[string]interface{}{
1711 "projectRef": map[string]interface{}{
1712 "external": "project-id-from-namespace-annotation",
1713 },
1714 },
1715 },
1716 },
1717 ns: &corev1.Namespace{
1718 ObjectMeta: metav1.ObjectMeta{
1719 Annotations: map[string]string{
1720 k8s.ProjectIDAnnotation: "project-id-from-namespace-annotation",
1721 },
1722 Name: "namespace-name",
1723 },
1724 },
1725 },
1726 {
1727 name: "default reference from namespace name (project-scoped resource which only supports hierarchical references)",
1728 obj: &unstructured.Unstructured{
1729 Object: map[string]interface{}{
1730 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1731 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1732 "metadata": map[string]interface{}{
1733 "name": "resource-name",
1734 },
1735 },
1736 },
1737 newObj: &unstructured.Unstructured{
1738 Object: map[string]interface{}{
1739 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1740 "kind": "Test4ProjectScopedResourceWithOnlyHierarchicalReferenceSupport",
1741 "metadata": map[string]interface{}{
1742 "name": "resource-name",
1743 },
1744 "spec": map[string]interface{}{
1745 "projectRef": map[string]interface{}{
1746 "external": "namespace-name",
1747 },
1748 },
1749 },
1750 },
1751 ns: &corev1.Namespace{
1752 ObjectMeta: metav1.ObjectMeta{
1753 Name: "namespace-name",
1754 },
1755 },
1756 },
1757 {
1758 name: "default reference from namespace annotation (multi-parent resource which only supports hierarchical references)",
1759 obj: &unstructured.Unstructured{
1760 Object: map[string]interface{}{
1761 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1762 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1763 "metadata": map[string]interface{}{
1764 "name": "resource-name",
1765 },
1766 },
1767 },
1768 newObj: &unstructured.Unstructured{
1769 Object: map[string]interface{}{
1770 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1771 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1772 "metadata": map[string]interface{}{
1773 "name": "resource-name",
1774 },
1775 "spec": map[string]interface{}{
1776 "folderRef": map[string]interface{}{
1777 "external": "folder-id-from-namespace-annotation",
1778 },
1779 },
1780 },
1781 },
1782 ns: &corev1.Namespace{
1783 ObjectMeta: metav1.ObjectMeta{
1784 Annotations: map[string]string{
1785 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1786 },
1787 Name: "namespace-name",
1788 },
1789 },
1790 },
1791 {
1792 name: "deny resource if more than one namespace annotation (multi-parent resource which only supports hierarchical references)",
1793 obj: &unstructured.Unstructured{
1794 Object: map[string]interface{}{
1795 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1796 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1797 "metadata": map[string]interface{}{
1798 "name": "resource-name",
1799 },
1800 },
1801 },
1802 ns: &corev1.Namespace{
1803 ObjectMeta: metav1.ObjectMeta{
1804 Annotations: map[string]string{
1805 k8s.FolderIDAnnotation: "folder-id-from-namespace-annotation",
1806 k8s.OrgIDAnnotation: "org-id-from-namespace-annotation",
1807 },
1808 Name: "namespace-name",
1809 },
1810 },
1811 denied: true,
1812 },
1813 {
1814 name: "deny resource if no namespace annotation (non-project-scoped resource which only supports hierarchical references)",
1815 obj: &unstructured.Unstructured{
1816 Object: map[string]interface{}{
1817 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1818 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1819 "metadata": map[string]interface{}{
1820 "name": "resource-name",
1821 },
1822 },
1823 },
1824 ns: &corev1.Namespace{
1825 ObjectMeta: metav1.ObjectMeta{
1826 Name: "namespace-name",
1827 },
1828 },
1829 denied: true,
1830 },
1831 {
1832 name: "allow resource even if no annotations found on resource/namespace as long as resource has reference (multi-parent resource which only supports hierarchical references)",
1833 obj: &unstructured.Unstructured{
1834 Object: map[string]interface{}{
1835 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1836 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1837 "metadata": map[string]interface{}{
1838 "name": "resource-name",
1839 },
1840 "spec": map[string]interface{}{
1841 "folderRef": map[string]interface{}{
1842 "name": "folder-id-from-spec",
1843 },
1844 },
1845 },
1846 },
1847 newObj: &unstructured.Unstructured{
1848 Object: map[string]interface{}{
1849 "apiVersion": "test4.cnrm.cloud.google.com/v1alpha1",
1850 "kind": "Test4MultiParentResourceWithOnlyHierarchicalReferenceSupport",
1851 "metadata": map[string]interface{}{
1852 "name": "resource-name",
1853 },
1854 "spec": map[string]interface{}{
1855 "folderRef": map[string]interface{}{
1856 "name": "folder-id-from-spec",
1857 },
1858 },
1859 },
1860 },
1861 ns: &corev1.Namespace{
1862 ObjectMeta: metav1.ObjectMeta{
1863 Name: "namespace-name",
1864 },
1865 },
1866 },
1867 }
1868
1869 smLoader := testservicemappingloader.NewForUnitTest(t)
1870 for _, tc := range tests {
1871 tc := tc
1872 t.Run(tc.name, func(t *testing.T) {
1873 t.Parallel()
1874 response := handleContainerAnnotationsForTFBasedResources(tc.obj, tc.ns, smLoader)
1875 if tc.denied {
1876 if response.Allowed {
1877 t.Fatalf("expected request to be denied, but was allowed. Response:\n%v", response)
1878 }
1879 return
1880 }
1881 if !response.Allowed {
1882 t.Fatalf("request was unexpectedly denied. Response:\n%v", response)
1883 }
1884 expectedResponse := constructPatchResponse(tc.obj, tc.newObj)
1885 if !testutil.Equals(t, expectedResponse, response) {
1886 diff := cmp.Diff(expectedResponse, response)
1887 t.Fatalf("unexpected diff in the response (-want +got):\n%v", diff)
1888 }
1889 })
1890 }
1891 }
1892
View as plain text