1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package kcclite_test
16
17 import (
18 "testing"
19
20 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
21 corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/kcclite"
23 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
24 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
25 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/servicemapping/servicemappingloader"
26 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
27 testcontroller "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/controller"
28 testdclschemaloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/dclschemaloader"
29 testvariable "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/resourcefixture/variable"
30 testservicemappingloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemappingloader"
31 testservicemetadataloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemetadataloader"
32 "github.com/nasa9084/go-openapi"
33 corev1 "k8s.io/api/core/v1"
34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
36 "k8s.io/apimachinery/pkg/runtime/schema"
37 )
38
39 var (
40 hierarchicalRefProject = corekccv1alpha1.HierarchicalReference{
41 Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
42 Key: "projectRef",
43 }
44 hierarchicalRefFolder = corekccv1alpha1.HierarchicalReference{
45 Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
46 Key: "folderRef",
47 }
48 hierarchicalRefOrganization = corekccv1alpha1.HierarchicalReference{
49 Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
50 Key: "organizationRef",
51 }
52 hierarchicalRefBillingAccount = corekccv1alpha1.HierarchicalReference{
53 Type: corekccv1alpha1.HierarchicalReferenceTypeBillingAccount,
54 Key: "billingAccountRef",
55 }
56
57 projectRefConfig = corekccv1alpha1.ReferenceConfig{
58 TFField: "project",
59 TypeConfig: corekccv1alpha1.TypeConfig{
60 Key: "projectRef",
61 GVK: schema.GroupVersionKind{
62 Group: "resourcemanager.cnrm.cloud.google.com",
63 Version: "v1beta1",
64 Kind: "Project",
65 },
66 },
67 }
68 folderRefConfig = corekccv1alpha1.ReferenceConfig{
69 TFField: "folder",
70 TypeConfig: corekccv1alpha1.TypeConfig{
71 Key: "folderRef",
72 GVK: schema.GroupVersionKind{
73 Group: "resourcemanager.cnrm.cloud.google.com",
74 Version: "v1beta1",
75 Kind: "Folder",
76 },
77 TargetField: "folder_id",
78 },
79 }
80 organizationRefConfig = corekccv1alpha1.ReferenceConfig{
81 TFField: "organization",
82 TypeConfig: corekccv1alpha1.TypeConfig{
83 Key: "organizationRef",
84 GVK: schema.GroupVersionKind{
85 Group: "resourcemanager.cnrm.cloud.google.com",
86 Version: "v1beta1",
87 Kind: "Organization",
88 },
89 },
90 }
91 billingAccountRefConfig = corekccv1alpha1.ReferenceConfig{
92 TFField: "billing_account",
93 TypeConfig: corekccv1alpha1.TypeConfig{
94 Key: "billingAccountRef",
95 GVK: schema.GroupVersionKind{
96 Group: "billing.cnrm.cloud.google.com",
97 Version: "v1beta1",
98 Kind: "BillingAccount",
99 },
100 },
101 }
102 )
103
104 func TestCanonicalizeReferencedResourceName(t *testing.T) {
105 tests := []struct {
106 name string
107 template string
108 refResource *k8s.Resource
109 refResourceSchema *openapi.Schema
110 refResourceReference *unstructured.Unstructured
111 expectedCanonName string
112 errorCheckFunc func(t *testing.T, err error)
113 }{
114 {
115 name: "template requires just {{name}}",
116 template: "{{name}}",
117 refResource: &k8s.Resource{
118 TypeMeta: metav1.TypeMeta{
119 APIVersion: "test1.cnrm.cloud.google.com/v1alpha1",
120 Kind: "Test1Foo",
121 },
122 },
123 expectedCanonName: "name",
124 },
125 {
126 name: "template requires top-level spec field",
127 template: "fields/{{field}}/names/{{name}}",
128 refResource: &k8s.Resource{
129 TypeMeta: metav1.TypeMeta{
130 APIVersion: "test1.cnrm.cloud.google.com/v1alpha1",
131 Kind: "Test1Foo",
132 },
133 Spec: map[string]interface{}{
134 "field": "val",
135 },
136 },
137 refResourceSchema: &openapi.Schema{
138 Type: "object",
139 Properties: map[string]*openapi.Schema{
140 "field": &openapi.Schema{
141 Type: "string",
142 },
143 },
144 },
145 expectedCanonName: "fields/val/names/name",
146 },
147 {
148 name: "template requires top-level spec field, but referenced resource doesn't have field in spec",
149 template: "fields/{{field}}/names/{{name}}",
150 refResource: &k8s.Resource{
151 TypeMeta: metav1.TypeMeta{
152 APIVersion: "test1.cnrm.cloud.google.com/v1alpha1",
153 Kind: "Test1Foo",
154 },
155 },
156 refResourceSchema: &openapi.Schema{
157 Type: "object",
158 Properties: map[string]*openapi.Schema{
159 "field": &openapi.Schema{
160 Type: "string",
161 },
162 },
163 },
164 errorCheckFunc: hasErrorCheckFunc,
165 },
166 {
167 name: "template requires parent of single-parent resource which only supports container annotations",
168 template: "projects/{{project}}/names/{{name}}",
169 refResource: &k8s.Resource{
170 TypeMeta: metav1.TypeMeta{
171 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
172 Kind: "Test6OnlyContainer",
173 },
174 ObjectMeta: metav1.ObjectMeta{
175 Annotations: map[string]string{
176 k8s.ProjectIDAnnotation: "project_id",
177 },
178 },
179 },
180 refResourceSchema: &openapi.Schema{
181 Type: "object",
182 Properties: map[string]*openapi.Schema{
183 "project": &openapi.Schema{
184 Type: "string",
185 Extension: map[string]interface{}{
186 "x-dcl-references": []interface{}{
187 map[interface{}]interface{}{
188 "field": "name",
189 "parent": true,
190 "resource": "Cloudresourcemanager/Project",
191 },
192 },
193 },
194 },
195 },
196 Extension: map[string]interface{}{
197 "x-dcl-parent-container": "project",
198 },
199 },
200 expectedCanonName: "projects/project_id/names/name",
201 },
202 {
203 name: "template requires parent of single-parent resource with no hierarchical reference in spec",
204 template: "projects/{{project}}/names/{{name}}",
205 refResource: &k8s.Resource{
206 TypeMeta: metav1.TypeMeta{
207 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
208 Kind: "Test6BothContainerAndHierarchicalRef",
209 },
210 },
211 refResourceSchema: &openapi.Schema{
212 Type: "object",
213 Properties: map[string]*openapi.Schema{
214 "project": &openapi.Schema{
215 Type: "string",
216 Extension: map[string]interface{}{
217 "x-dcl-references": []interface{}{
218 map[interface{}]interface{}{
219 "field": "name",
220 "parent": true,
221 "resource": "Cloudresourcemanager/Project",
222 },
223 },
224 },
225 },
226 },
227 Extension: map[string]interface{}{
228 "x-dcl-parent-container": "project",
229 },
230 },
231 errorCheckFunc: hasErrorCheckFunc,
232 },
233 {
234 name: "template requires parent of single-parent resource with external project reference",
235 template: "projects/{{project}}/names/{{name}}",
236 refResource: &k8s.Resource{
237 TypeMeta: metav1.TypeMeta{
238 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
239 Kind: "Test6BothContainerAndHierarchicalRef",
240 },
241 Spec: map[string]interface{}{
242 "projectRef": map[string]interface{}{
243 "external": "project_id",
244 },
245 },
246 },
247 refResourceSchema: &openapi.Schema{
248 Type: "object",
249 Properties: map[string]*openapi.Schema{
250 "project": &openapi.Schema{
251 Type: "string",
252 Extension: map[string]interface{}{
253 "x-dcl-references": []interface{}{
254 map[interface{}]interface{}{
255 "field": "name",
256 "parent": true,
257 "resource": "Cloudresourcemanager/Project",
258 },
259 },
260 },
261 },
262 },
263 Extension: map[string]interface{}{
264 "x-dcl-parent-container": "project",
265 },
266 },
267 expectedCanonName: "projects/project_id/names/name",
268 },
269 {
270 name: "template requires parent of single-parent resource with external project reference that is set to a path",
271 template: "projects/{{project}}/names/{{name}}",
272 refResource: &k8s.Resource{
273 TypeMeta: metav1.TypeMeta{
274 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
275 Kind: "Test6BothContainerAndHierarchicalRef",
276 },
277 Spec: map[string]interface{}{
278 "projectRef": map[string]interface{}{
279 "external": "projects/project_id",
280 },
281 },
282 },
283 refResourceSchema: &openapi.Schema{
284 Type: "object",
285 Properties: map[string]*openapi.Schema{
286 "project": &openapi.Schema{
287 Type: "string",
288 Extension: map[string]interface{}{
289 "x-dcl-references": []interface{}{
290 map[interface{}]interface{}{
291 "field": "name",
292 "parent": true,
293 "resource": "Cloudresourcemanager/Project",
294 },
295 },
296 },
297 },
298 },
299 Extension: map[string]interface{}{
300 "x-dcl-parent-container": "project",
301 },
302 },
303 expectedCanonName: "projects/project_id/names/name",
304 },
305 {
306 name: "template requires parent of single-parent resource with project reference to non-existent Project",
307 template: "projects/{{project}}/names/{{name}}",
308 refResource: &k8s.Resource{
309 TypeMeta: metav1.TypeMeta{
310 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
311 Kind: "Test6BothContainerAndHierarchicalRef",
312 },
313 Spec: map[string]interface{}{
314 "projectRef": map[string]interface{}{
315 "name": "project-name",
316 },
317 },
318 },
319 refResourceSchema: &openapi.Schema{
320 Type: "object",
321 Properties: map[string]*openapi.Schema{
322 "project": &openapi.Schema{
323 Type: "string",
324 Extension: map[string]interface{}{
325 "x-dcl-references": []interface{}{
326 map[interface{}]interface{}{
327 "field": "name",
328 "parent": true,
329 "resource": "Cloudresourcemanager/Project",
330 },
331 },
332 },
333 },
334 },
335 Extension: map[string]interface{}{
336 "x-dcl-parent-container": "project",
337 },
338 },
339 errorCheckFunc: transDepNotFoundErrorCheckFunc,
340 },
341 {
342 name: "template requires parent of single-parent resource with project reference to non-ready Project",
343 template: "projects/{{project}}/names/{{name}}",
344 refResource: &k8s.Resource{
345 TypeMeta: metav1.TypeMeta{
346 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
347 Kind: "Test6BothContainerAndHierarchicalRef",
348 },
349 Spec: map[string]interface{}{
350 "projectRef": map[string]interface{}{
351 "name": "project-name",
352 },
353 },
354 },
355 refResourceSchema: &openapi.Schema{
356 Type: "object",
357 Properties: map[string]*openapi.Schema{
358 "project": &openapi.Schema{
359 Type: "string",
360 Extension: map[string]interface{}{
361 "x-dcl-references": []interface{}{
362 map[interface{}]interface{}{
363 "field": "name",
364 "parent": true,
365 "resource": "Cloudresourcemanager/Project",
366 },
367 },
368 },
369 },
370 },
371 Extension: map[string]interface{}{
372 "x-dcl-parent-container": "project",
373 },
374 },
375 refResourceReference: test.NewProjectUnstructured("project-name", "project_id", corev1.ConditionFalse),
376 errorCheckFunc: transDepNotReadyErrorCheckFunc,
377 },
378 {
379 name: "template requires parent of single-parent resource with project reference",
380 template: "projects/{{project}}/names/{{name}}",
381 refResource: &k8s.Resource{
382 TypeMeta: metav1.TypeMeta{
383 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
384 Kind: "Test6BothContainerAndHierarchicalRef",
385 },
386 Spec: map[string]interface{}{
387 "projectRef": map[string]interface{}{
388 "name": "project-name",
389 },
390 },
391 },
392 refResourceSchema: &openapi.Schema{
393 Type: "object",
394 Properties: map[string]*openapi.Schema{
395 "project": &openapi.Schema{
396 Type: "string",
397 Extension: map[string]interface{}{
398 "x-dcl-references": []interface{}{
399 map[interface{}]interface{}{
400 "field": "name",
401 "parent": true,
402 "resource": "Cloudresourcemanager/Project",
403 },
404 },
405 },
406 },
407 },
408 Extension: map[string]interface{}{
409 "x-dcl-parent-container": "project",
410 },
411 },
412 refResourceReference: test.NewProjectUnstructured("project-name", "project_id", corev1.ConditionTrue),
413 expectedCanonName: "projects/project_id/names/name",
414 },
415 {
416 name: "template requires parent of single-parent resource with project reference that resolved to a path",
417 template: "projects/{{project}}/names/{{name}}",
418 refResource: &k8s.Resource{
419 TypeMeta: metav1.TypeMeta{
420 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
421 Kind: "Test6BothContainerAndHierarchicalRef",
422 },
423 Spec: map[string]interface{}{
424 "projectRef": map[string]interface{}{
425 "name": "project-name",
426 },
427 },
428 },
429 refResourceSchema: &openapi.Schema{
430 Type: "object",
431 Properties: map[string]*openapi.Schema{
432 "project": &openapi.Schema{
433 Type: "string",
434 Extension: map[string]interface{}{
435 "x-dcl-references": []interface{}{
436 map[interface{}]interface{}{
437 "field": "name",
438 "parent": true,
439 "resource": "Cloudresourcemanager/Project",
440 },
441 },
442 },
443 },
444 },
445 Extension: map[string]interface{}{
446 "x-dcl-parent-container": "project",
447 },
448 },
449 refResourceReference: test.NewProjectUnstructured("project-name", "projects/project_id", corev1.ConditionTrue),
450 expectedCanonName: "projects/project_id/names/name",
451 },
452 {
453 name: "template requires parent of multi-parent resource with no hierarchical reference in spec",
454 template: "{{parent}}/names/{{name}}",
455 refResource: &k8s.Resource{
456 TypeMeta: metav1.TypeMeta{
457 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
458 Kind: "Test6OnlyHierarchicalRef",
459 },
460 },
461 refResourceSchema: &openapi.Schema{
462 Type: "object",
463 Properties: map[string]*openapi.Schema{
464 "parent": &openapi.Schema{
465 Type: "string",
466 Extension: map[string]interface{}{
467 "x-dcl-references": []interface{}{
468 map[interface{}]interface{}{
469 "field": "name",
470 "parent": true,
471 "resource": "Cloudresourcemanager/Project",
472 },
473 map[interface{}]interface{}{
474 "field": "name",
475 "parent": true,
476 "resource": "Cloudresourcemanager/Folder",
477 },
478 map[interface{}]interface{}{
479 "field": "name",
480 "parent": true,
481 "resource": "Cloudresourcemanager/Organization",
482 },
483 },
484 },
485 },
486 },
487 },
488 errorCheckFunc: hasErrorCheckFunc,
489 },
490 {
491 name: "template requires parent of multi-parent resource with external folder reference",
492 template: "{{parent}}/names/{{name}}",
493 refResource: &k8s.Resource{
494 TypeMeta: metav1.TypeMeta{
495 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
496 Kind: "Test6OnlyHierarchicalRef",
497 },
498 Spec: map[string]interface{}{
499 "folderRef": map[string]interface{}{
500 "external": "folder_id",
501 },
502 },
503 },
504 refResourceSchema: &openapi.Schema{
505 Type: "object",
506 Properties: map[string]*openapi.Schema{
507 "parent": &openapi.Schema{
508 Type: "string",
509 Extension: map[string]interface{}{
510 "x-dcl-references": []interface{}{
511 map[interface{}]interface{}{
512 "field": "name",
513 "parent": true,
514 "resource": "Cloudresourcemanager/Project",
515 },
516 map[interface{}]interface{}{
517 "field": "name",
518 "parent": true,
519 "resource": "Cloudresourcemanager/Folder",
520 },
521 map[interface{}]interface{}{
522 "field": "name",
523 "parent": true,
524 "resource": "Cloudresourcemanager/Organization",
525 },
526 },
527 },
528 },
529 },
530 },
531 expectedCanonName: "folders/folder_id/names/name",
532 },
533 {
534 name: "template requires parent of multi-parent resource with external folder reference that is set to a path",
535 template: "{{parent}}/names/{{name}}",
536 refResource: &k8s.Resource{
537 TypeMeta: metav1.TypeMeta{
538 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
539 Kind: "Test6OnlyHierarchicalRef",
540 },
541 Spec: map[string]interface{}{
542 "folderRef": map[string]interface{}{
543 "external": "folders/folder_id",
544 },
545 },
546 },
547 refResourceSchema: &openapi.Schema{
548 Type: "object",
549 Properties: map[string]*openapi.Schema{
550 "parent": &openapi.Schema{
551 Type: "string",
552 Extension: map[string]interface{}{
553 "x-dcl-references": []interface{}{
554 map[interface{}]interface{}{
555 "field": "name",
556 "parent": true,
557 "resource": "Cloudresourcemanager/Project",
558 },
559 map[interface{}]interface{}{
560 "field": "name",
561 "parent": true,
562 "resource": "Cloudresourcemanager/Folder",
563 },
564 map[interface{}]interface{}{
565 "field": "name",
566 "parent": true,
567 "resource": "Cloudresourcemanager/Organization",
568 },
569 },
570 },
571 },
572 },
573 },
574 expectedCanonName: "folders/folder_id/names/name",
575 },
576 {
577 name: "template requires parent of multi-parent resource with folder reference to non-existent Folder",
578 template: "{{parent}}/names/{{name}}",
579 refResource: &k8s.Resource{
580 TypeMeta: metav1.TypeMeta{
581 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
582 Kind: "Test6OnlyHierarchicalRef",
583 },
584 Spec: map[string]interface{}{
585 "folderRef": map[string]interface{}{
586 "name": "folder-name",
587 },
588 },
589 },
590 refResourceSchema: &openapi.Schema{
591 Type: "object",
592 Properties: map[string]*openapi.Schema{
593 "parent": &openapi.Schema{
594 Type: "string",
595 Extension: map[string]interface{}{
596 "x-dcl-references": []interface{}{
597 map[interface{}]interface{}{
598 "field": "name",
599 "parent": true,
600 "resource": "Cloudresourcemanager/Project",
601 },
602 map[interface{}]interface{}{
603 "field": "name",
604 "parent": true,
605 "resource": "Cloudresourcemanager/Folder",
606 },
607 map[interface{}]interface{}{
608 "field": "name",
609 "parent": true,
610 "resource": "Cloudresourcemanager/Organization",
611 },
612 },
613 },
614 },
615 },
616 },
617 errorCheckFunc: transDepNotFoundErrorCheckFunc,
618 },
619 {
620 name: "template requires parent of multi-parent resource with folder reference to non-ready Folder",
621 template: "{{parent}}/names/{{name}}",
622 refResource: &k8s.Resource{
623 TypeMeta: metav1.TypeMeta{
624 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
625 Kind: "Test6OnlyHierarchicalRef",
626 },
627 Spec: map[string]interface{}{
628 "folderRef": map[string]interface{}{
629 "name": "folder-name",
630 },
631 },
632 },
633 refResourceSchema: &openapi.Schema{
634 Type: "object",
635 Properties: map[string]*openapi.Schema{
636 "parent": &openapi.Schema{
637 Type: "string",
638 Extension: map[string]interface{}{
639 "x-dcl-references": []interface{}{
640 map[interface{}]interface{}{
641 "field": "name",
642 "parent": true,
643 "resource": "Cloudresourcemanager/Project",
644 },
645 map[interface{}]interface{}{
646 "field": "name",
647 "parent": true,
648 "resource": "Cloudresourcemanager/Folder",
649 },
650 map[interface{}]interface{}{
651 "field": "name",
652 "parent": true,
653 "resource": "Cloudresourcemanager/Organization",
654 },
655 },
656 },
657 },
658 },
659 },
660 refResourceReference: test.NewFolderUnstructured("folder-name", "folder_id", corev1.ConditionFalse),
661 errorCheckFunc: transDepNotReadyErrorCheckFunc,
662 },
663 {
664 name: "template requires parent of multi-parent resource with folder reference",
665 template: "{{parent}}/names/{{name}}",
666 refResource: &k8s.Resource{
667 TypeMeta: metav1.TypeMeta{
668 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
669 Kind: "Test6OnlyHierarchicalRef",
670 },
671 Spec: map[string]interface{}{
672 "folderRef": map[string]interface{}{
673 "name": "folder-name",
674 },
675 },
676 },
677 refResourceSchema: &openapi.Schema{
678 Type: "object",
679 Properties: map[string]*openapi.Schema{
680 "parent": &openapi.Schema{
681 Type: "string",
682 Extension: map[string]interface{}{
683 "x-dcl-references": []interface{}{
684 map[interface{}]interface{}{
685 "field": "name",
686 "parent": true,
687 "resource": "Cloudresourcemanager/Project",
688 },
689 map[interface{}]interface{}{
690 "field": "name",
691 "parent": true,
692 "resource": "Cloudresourcemanager/Folder",
693 },
694 map[interface{}]interface{}{
695 "field": "name",
696 "parent": true,
697 "resource": "Cloudresourcemanager/Organization",
698 },
699 },
700 },
701 },
702 },
703 },
704 refResourceReference: test.NewFolderUnstructured("folder-name", "folder_id", corev1.ConditionTrue),
705 expectedCanonName: "folders/folder_id/names/name",
706 },
707 {
708 name: "template requires parent of multi-parent resource with folder reference that resolves to a path",
709 template: "{{parent}}/names/{{name}}",
710 refResource: &k8s.Resource{
711 TypeMeta: metav1.TypeMeta{
712 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
713 Kind: "Test6OnlyHierarchicalRef",
714 },
715 Spec: map[string]interface{}{
716 "folderRef": map[string]interface{}{
717 "name": "folder-name",
718 },
719 },
720 },
721 refResourceSchema: &openapi.Schema{
722 Type: "object",
723 Properties: map[string]*openapi.Schema{
724 "parent": &openapi.Schema{
725 Type: "string",
726 Extension: map[string]interface{}{
727 "x-dcl-references": []interface{}{
728 map[interface{}]interface{}{
729 "field": "name",
730 "parent": true,
731 "resource": "Cloudresourcemanager/Project",
732 },
733 map[interface{}]interface{}{
734 "field": "name",
735 "parent": true,
736 "resource": "Cloudresourcemanager/Folder",
737 },
738 map[interface{}]interface{}{
739 "field": "name",
740 "parent": true,
741 "resource": "Cloudresourcemanager/Organization",
742 },
743 },
744 },
745 },
746 },
747 },
748 refResourceReference: test.NewFolderUnstructured("folder-name", "folders/folder_id", corev1.ConditionTrue),
749 expectedCanonName: "folders/folder_id/names/name",
750 },
751 {
752 name: "template requires parent of multi-parent resource with external billing account reference that is set to a path",
753 template: "{{parent}}/names/{{name}}",
754 refResource: &k8s.Resource{
755 TypeMeta: metav1.TypeMeta{
756 APIVersion: "test6.cnrm.cloud.google.com/v1alpha1",
757 Kind: "Test6OnlyHierarchicalRef",
758 },
759 Spec: map[string]interface{}{
760 "billingAccountRef": map[string]interface{}{
761 "external": "billingAccounts/billing_account_id",
762 },
763 },
764 },
765 refResourceSchema: &openapi.Schema{
766 Type: "object",
767 Properties: map[string]*openapi.Schema{
768 "parent": &openapi.Schema{
769 Type: "string",
770 Extension: map[string]interface{}{
771 "x-dcl-references": []interface{}{
772 map[interface{}]interface{}{
773 "field": "name",
774 "parent": true,
775 "resource": "Cloudresourcemanager/Project",
776 },
777 map[interface{}]interface{}{
778 "field": "name",
779 "parent": true,
780 "resource": "Cloudresourcemanager/Folder",
781 },
782 map[interface{}]interface{}{
783 "field": "name",
784 "parent": true,
785 "resource": "Cloudresourcemanager/Organization",
786 },
787 map[interface{}]interface{}{
788 "field": "name",
789 "parent": true,
790 "resource": "Cloudresourcemanager/BillingAccount",
791 },
792 },
793 },
794 },
795 },
796 },
797 expectedCanonName: "billingAccounts/billing_account_id/names/name",
798 },
799 }
800
801 smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
802 serviceMappingLoader := testservicemappingloader.NewForUnitTest(t)
803 for _, tc := range tests {
804 tc := tc
805 t.Run(tc.name, func(t *testing.T) {
806 t.Parallel()
807 testId := testvariable.NewUniqueId()
808 c := mgr.GetClient()
809 testcontroller.EnsureNamespaceExists(c, testId)
810 tc.refResource.SetNamespace(testId)
811 if tc.refResourceReference != nil {
812 tc.refResourceReference.SetNamespace(testId)
813 test.EnsureObjectExists(t, tc.refResourceReference, c)
814 }
815
816 schemaKey := testdclschemaloader.DCLSchemaKeyForGVK(t, tc.refResource.GroupVersionKind(), smLoader)
817 schemaMap := map[string]*openapi.Schema{
818 schemaKey: tc.refResourceSchema,
819 }
820 schemaLoader := testdclschemaloader.New(schemaMap)
821
822 canonName, err := kcclite.CanonicalizeReferencedResourceName("name", tc.template, tc.refResource, smLoader, schemaLoader, serviceMappingLoader, c)
823 if tc.errorCheckFunc != nil {
824 tc.errorCheckFunc(t, err)
825 return
826 }
827 if err != nil {
828 t.Fatalf("got error, wanted none: %v", err)
829 }
830 if canonName != tc.expectedCanonName {
831 t.Fatalf("got %v, want %v", canonName, tc.expectedCanonName)
832 }
833 })
834 }
835 }
836
837 func TestCanonicalizeReferencedResourceNameForTFBasedResource(t *testing.T) {
838 tests := []struct {
839 name string
840 template string
841 refResource *k8s.Resource
842 refResourceConfig corekccv1alpha1.ResourceConfig
843 refResourceReference *unstructured.Unstructured
844 expectedCanonName string
845 errorCheckFunc func(t *testing.T, err error)
846 }{
847 {
848 name: "template requires just {{name}}",
849 template: "{{name}}",
850 refResource: &k8s.Resource{
851 TypeMeta: metav1.TypeMeta{
852 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
853 Kind: "TFOnlyKind",
854 },
855 },
856 refResourceConfig: corekccv1alpha1.ResourceConfig{
857 Name: "tf_only_kind",
858 Kind: "TFOnlyKind",
859 },
860 expectedCanonName: "name",
861 },
862 {
863 name: "template requires top-level spec field",
864 template: "fields/{{field}}/names/{{name}}",
865 refResource: &k8s.Resource{
866 TypeMeta: metav1.TypeMeta{
867 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
868 Kind: "TFOnlyKind",
869 },
870 Spec: map[string]interface{}{
871 "field": "val",
872 },
873 },
874 refResourceConfig: corekccv1alpha1.ResourceConfig{
875 Name: "tf_only_kind",
876 Kind: "TFOnlyKind",
877 },
878 expectedCanonName: "fields/val/names/name",
879 },
880 {
881 name: "template requires top-level spec field, but referenced resource doesn't have field in spec",
882 template: "fields/{{field}}/names/{{name}}",
883 refResource: &k8s.Resource{
884 TypeMeta: metav1.TypeMeta{
885 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
886 Kind: "TFOnlyKind",
887 },
888 },
889 refResourceConfig: corekccv1alpha1.ResourceConfig{
890 Name: "tf_only_kind",
891 Kind: "TFOnlyKind",
892 },
893 errorCheckFunc: hasErrorCheckFunc,
894 },
895 {
896 name: "template requires parent of single-parent resource which only supports container annotations",
897 template: "projects/{{project}}/names/{{name}}",
898 refResource: &k8s.Resource{
899 TypeMeta: metav1.TypeMeta{
900 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
901 Kind: "TFOnlyKind",
902 },
903 ObjectMeta: metav1.ObjectMeta{
904 Annotations: map[string]string{
905 k8s.ProjectIDAnnotation: "project_id",
906 },
907 },
908 },
909 refResourceConfig: corekccv1alpha1.ResourceConfig{
910 Name: "tf_only_kind",
911 Kind: "TFOnlyKind",
912 Containers: []corekccv1alpha1.Container{
913 {Type: corekccv1alpha1.ContainerTypeProject},
914 },
915 },
916 expectedCanonName: "projects/project_id/names/name",
917 },
918 {
919 name: "template requires parent of single-parent resource with no hierarchical reference in spec",
920 template: "projects/{{project}}/names/{{name}}",
921 refResource: &k8s.Resource{
922 TypeMeta: metav1.TypeMeta{
923 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
924 Kind: "TFOnlyKind",
925 },
926 },
927 refResourceConfig: corekccv1alpha1.ResourceConfig{
928 Name: "tf_only_kind",
929 Kind: "TFOnlyKind",
930 Containers: []corekccv1alpha1.Container{
931 {Type: corekccv1alpha1.ContainerTypeProject},
932 },
933 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
934 hierarchicalRefProject,
935 },
936 },
937 errorCheckFunc: hasErrorCheckFunc,
938 },
939 {
940 name: "template requires parent of single-parent resource with external project reference",
941 template: "projects/{{project}}/names/{{name}}",
942 refResource: &k8s.Resource{
943 TypeMeta: metav1.TypeMeta{
944 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
945 Kind: "TFOnlyKind",
946 },
947 Spec: map[string]interface{}{
948 "projectRef": map[string]interface{}{
949 "external": "project_id",
950 },
951 },
952 },
953 refResourceConfig: corekccv1alpha1.ResourceConfig{
954 Name: "tf_only_kind",
955 Kind: "TFOnlyKind",
956 Containers: []corekccv1alpha1.Container{
957 {Type: corekccv1alpha1.ContainerTypeProject},
958 },
959 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
960 hierarchicalRefProject,
961 },
962 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
963 projectRefConfig,
964 },
965 },
966 expectedCanonName: "projects/project_id/names/name",
967 },
968 {
969 name: "template requires parent of single-parent resource with external project reference that is set to a path",
970 template: "projects/{{project}}/names/{{name}}",
971 refResource: &k8s.Resource{
972 TypeMeta: metav1.TypeMeta{
973 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
974 Kind: "TFOnlyKind",
975 },
976 Spec: map[string]interface{}{
977 "projectRef": map[string]interface{}{
978 "external": "projects/project_id",
979 },
980 },
981 },
982 refResourceConfig: corekccv1alpha1.ResourceConfig{
983 Name: "tf_only_kind",
984 Kind: "TFOnlyKind",
985 Containers: []corekccv1alpha1.Container{
986 {Type: corekccv1alpha1.ContainerTypeProject},
987 },
988 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
989 hierarchicalRefProject,
990 },
991 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
992 projectRefConfig,
993 },
994 },
995 expectedCanonName: "projects/project_id/names/name",
996 },
997 {
998 name: "template requires parent of single-parent resource with project reference to non-existent Project",
999 template: "projects/{{project}}/names/{{name}}",
1000 refResource: &k8s.Resource{
1001 TypeMeta: metav1.TypeMeta{
1002 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1003 Kind: "TFOnlyKind",
1004 },
1005 Spec: map[string]interface{}{
1006 "projectRef": map[string]interface{}{
1007 "name": "project-name",
1008 },
1009 },
1010 },
1011 refResourceConfig: corekccv1alpha1.ResourceConfig{
1012 Name: "tf_only_kind",
1013 Kind: "TFOnlyKind",
1014 Containers: []corekccv1alpha1.Container{
1015 {Type: corekccv1alpha1.ContainerTypeProject},
1016 },
1017 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1018 hierarchicalRefProject,
1019 },
1020 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1021 projectRefConfig,
1022 },
1023 },
1024 errorCheckFunc: transDepNotFoundErrorCheckFunc,
1025 },
1026 {
1027 name: "template requires parent of single-parent resource with project reference to non-ready Project",
1028 template: "projects/{{project}}/names/{{name}}",
1029 refResource: &k8s.Resource{
1030 TypeMeta: metav1.TypeMeta{
1031 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1032 Kind: "TFOnlyKind",
1033 },
1034 Spec: map[string]interface{}{
1035 "projectRef": map[string]interface{}{
1036 "name": "project-name",
1037 },
1038 },
1039 },
1040 refResourceConfig: corekccv1alpha1.ResourceConfig{
1041 Name: "tf_only_kind",
1042 Kind: "TFOnlyKind",
1043 Containers: []corekccv1alpha1.Container{
1044 {Type: corekccv1alpha1.ContainerTypeProject},
1045 },
1046 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1047 hierarchicalRefProject,
1048 },
1049 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1050 projectRefConfig,
1051 },
1052 },
1053 refResourceReference: test.NewProjectUnstructured("project-name", "project_id", corev1.ConditionFalse),
1054 errorCheckFunc: transDepNotReadyErrorCheckFunc,
1055 },
1056 {
1057 name: "template requires parent of single-parent resource with project reference",
1058 template: "projects/{{project}}/names/{{name}}",
1059 refResource: &k8s.Resource{
1060 TypeMeta: metav1.TypeMeta{
1061 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1062 Kind: "TFOnlyKind",
1063 },
1064 Spec: map[string]interface{}{
1065 "projectRef": map[string]interface{}{
1066 "name": "project-name",
1067 },
1068 },
1069 },
1070 refResourceConfig: corekccv1alpha1.ResourceConfig{
1071 Name: "tf_only_kind",
1072 Kind: "TFOnlyKind",
1073 Containers: []corekccv1alpha1.Container{
1074 {Type: corekccv1alpha1.ContainerTypeProject},
1075 },
1076 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1077 hierarchicalRefProject,
1078 },
1079 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1080 projectRefConfig,
1081 },
1082 },
1083 refResourceReference: test.NewProjectUnstructured("project-name", "project_id", corev1.ConditionTrue),
1084 expectedCanonName: "projects/project_id/names/name",
1085 },
1086 {
1087 name: "template requires parent of single-parent resource with folder reference that resolved to a path",
1088 template: "folders/{{folder}}/names/{{name}}",
1089 refResource: &k8s.Resource{
1090 TypeMeta: metav1.TypeMeta{
1091 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1092 Kind: "TFOnlyKind",
1093 },
1094 Spec: map[string]interface{}{
1095 "folderRef": map[string]interface{}{
1096 "name": "folder-name",
1097 },
1098 },
1099 },
1100 refResourceConfig: corekccv1alpha1.ResourceConfig{
1101 Name: "tf_only_kind",
1102 Kind: "TFOnlyKind",
1103 Containers: []corekccv1alpha1.Container{
1104 {Type: corekccv1alpha1.ContainerTypeFolder},
1105 },
1106 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1107 hierarchicalRefFolder,
1108 },
1109 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1110 folderRefConfig,
1111 },
1112 },
1113 refResourceReference: test.NewFolderUnstructured("folder-name", "folders/folder_id", corev1.ConditionTrue),
1114 expectedCanonName: "folders/folder_id/names/name",
1115 },
1116 {
1117 name: "template requires parent of multi-parent resource with no hierarchical reference in spec",
1118 template: "{{parent}}/names/{{name}}",
1119 refResource: &k8s.Resource{
1120 TypeMeta: metav1.TypeMeta{
1121 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1122 Kind: "TFOnlyKind",
1123 },
1124 },
1125 refResourceConfig: corekccv1alpha1.ResourceConfig{
1126 Name: "tf_only_kind",
1127 Kind: "TFOnlyKind",
1128 Containers: []corekccv1alpha1.Container{
1129 {Type: corekccv1alpha1.ContainerTypeProject},
1130 {Type: corekccv1alpha1.ContainerTypeFolder},
1131 {Type: corekccv1alpha1.ContainerTypeOrganization},
1132 },
1133 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1134 hierarchicalRefProject,
1135 hierarchicalRefFolder,
1136 hierarchicalRefOrganization,
1137 },
1138 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1139 projectRefConfig,
1140 folderRefConfig,
1141 organizationRefConfig,
1142 },
1143 },
1144 errorCheckFunc: hasErrorCheckFunc,
1145 },
1146 {
1147 name: "template requires parent of multi-parent resource with external folder reference",
1148 template: "{{parent}}/names/{{name}}",
1149 refResource: &k8s.Resource{
1150 TypeMeta: metav1.TypeMeta{
1151 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1152 Kind: "TFOnlyKind",
1153 },
1154 Spec: map[string]interface{}{
1155 "folderRef": map[string]interface{}{
1156 "external": "folder_id",
1157 },
1158 },
1159 },
1160 refResourceConfig: corekccv1alpha1.ResourceConfig{
1161 Name: "tf_only_kind",
1162 Kind: "TFOnlyKind",
1163 Containers: []corekccv1alpha1.Container{
1164 {Type: corekccv1alpha1.ContainerTypeProject},
1165 {Type: corekccv1alpha1.ContainerTypeFolder},
1166 {Type: corekccv1alpha1.ContainerTypeOrganization},
1167 },
1168 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1169 hierarchicalRefProject,
1170 hierarchicalRefFolder,
1171 hierarchicalRefOrganization,
1172 },
1173 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1174 projectRefConfig,
1175 folderRefConfig,
1176 organizationRefConfig,
1177 },
1178 },
1179 expectedCanonName: "folders/folder_id/names/name",
1180 },
1181 {
1182 name: "template requires parent of multi-parent resource with external folder reference that is set to a path",
1183 template: "{{parent}}/names/{{name}}",
1184 refResource: &k8s.Resource{
1185 TypeMeta: metav1.TypeMeta{
1186 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1187 Kind: "TFOnlyKind",
1188 },
1189 Spec: map[string]interface{}{
1190 "folderRef": map[string]interface{}{
1191 "external": "folders/folder_id",
1192 },
1193 },
1194 },
1195 refResourceConfig: corekccv1alpha1.ResourceConfig{
1196 Name: "tf_only_kind",
1197 Kind: "TFOnlyKind",
1198 Containers: []corekccv1alpha1.Container{
1199 {Type: corekccv1alpha1.ContainerTypeProject},
1200 {Type: corekccv1alpha1.ContainerTypeFolder},
1201 {Type: corekccv1alpha1.ContainerTypeOrganization},
1202 },
1203 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1204 hierarchicalRefProject,
1205 hierarchicalRefFolder,
1206 hierarchicalRefOrganization,
1207 },
1208 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1209 projectRefConfig,
1210 folderRefConfig,
1211 organizationRefConfig,
1212 },
1213 },
1214 expectedCanonName: "folders/folder_id/names/name",
1215 },
1216 {
1217 name: "template requires parent of multi-parent resource with folder reference to non-existent Folder",
1218 template: "{{parent}}/names/{{name}}",
1219 refResource: &k8s.Resource{
1220 TypeMeta: metav1.TypeMeta{
1221 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1222 Kind: "TFOnlyKind",
1223 },
1224 Spec: map[string]interface{}{
1225 "folderRef": map[string]interface{}{
1226 "name": "folder-name",
1227 },
1228 },
1229 },
1230 refResourceConfig: corekccv1alpha1.ResourceConfig{
1231 Name: "tf_only_kind",
1232 Kind: "TFOnlyKind",
1233 Containers: []corekccv1alpha1.Container{
1234 {Type: corekccv1alpha1.ContainerTypeProject},
1235 {Type: corekccv1alpha1.ContainerTypeFolder},
1236 {Type: corekccv1alpha1.ContainerTypeOrganization},
1237 },
1238 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1239 hierarchicalRefProject,
1240 hierarchicalRefFolder,
1241 hierarchicalRefOrganization,
1242 },
1243 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1244 projectRefConfig,
1245 folderRefConfig,
1246 organizationRefConfig,
1247 },
1248 },
1249 errorCheckFunc: transDepNotFoundErrorCheckFunc,
1250 },
1251 {
1252 name: "template requires parent of multi-parent resource with folder reference to non-ready Folder",
1253 template: "{{parent}}/names/{{name}}",
1254 refResource: &k8s.Resource{
1255 TypeMeta: metav1.TypeMeta{
1256 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1257 Kind: "TFOnlyKind",
1258 },
1259 Spec: map[string]interface{}{
1260 "folderRef": map[string]interface{}{
1261 "name": "folder-name",
1262 },
1263 },
1264 },
1265 refResourceConfig: corekccv1alpha1.ResourceConfig{
1266 Name: "tf_only_kind",
1267 Kind: "TFOnlyKind",
1268 Containers: []corekccv1alpha1.Container{
1269 {Type: corekccv1alpha1.ContainerTypeProject},
1270 {Type: corekccv1alpha1.ContainerTypeFolder},
1271 {Type: corekccv1alpha1.ContainerTypeOrganization},
1272 },
1273 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1274 hierarchicalRefProject,
1275 hierarchicalRefFolder,
1276 hierarchicalRefOrganization,
1277 },
1278 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1279 projectRefConfig,
1280 folderRefConfig,
1281 organizationRefConfig,
1282 },
1283 },
1284 refResourceReference: test.NewFolderUnstructured("folder-name", "folder_id", corev1.ConditionFalse),
1285 errorCheckFunc: transDepNotReadyErrorCheckFunc,
1286 },
1287 {
1288 name: "template requires parent of multi-parent resource with folder reference",
1289 template: "{{parent}}/names/{{name}}",
1290 refResource: &k8s.Resource{
1291 TypeMeta: metav1.TypeMeta{
1292 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1293 Kind: "TFOnlyKind",
1294 },
1295 Spec: map[string]interface{}{
1296 "folderRef": map[string]interface{}{
1297 "name": "folder-name",
1298 },
1299 },
1300 },
1301 refResourceConfig: corekccv1alpha1.ResourceConfig{
1302 Name: "tf_only_kind",
1303 Kind: "TFOnlyKind",
1304 Containers: []corekccv1alpha1.Container{
1305 {Type: corekccv1alpha1.ContainerTypeProject},
1306 {Type: corekccv1alpha1.ContainerTypeFolder},
1307 {Type: corekccv1alpha1.ContainerTypeOrganization},
1308 },
1309 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1310 hierarchicalRefProject,
1311 hierarchicalRefFolder,
1312 hierarchicalRefOrganization,
1313 },
1314 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1315 projectRefConfig,
1316 folderRefConfig,
1317 organizationRefConfig,
1318 },
1319 },
1320 refResourceReference: test.NewFolderUnstructured("folder-name", "folder_id", corev1.ConditionTrue),
1321 expectedCanonName: "folders/folder_id/names/name",
1322 },
1323 {
1324 name: "template requires parent of multi-parent resource with folder reference that resolves to a path",
1325 template: "{{parent}}/names/{{name}}",
1326 refResource: &k8s.Resource{
1327 TypeMeta: metav1.TypeMeta{
1328 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1329 Kind: "TFOnlyKind",
1330 },
1331 Spec: map[string]interface{}{
1332 "folderRef": map[string]interface{}{
1333 "name": "folder-name",
1334 },
1335 },
1336 },
1337 refResourceConfig: corekccv1alpha1.ResourceConfig{
1338 Name: "tf_only_kind",
1339 Kind: "TFOnlyKind",
1340 Containers: []corekccv1alpha1.Container{
1341 {Type: corekccv1alpha1.ContainerTypeProject},
1342 {Type: corekccv1alpha1.ContainerTypeFolder},
1343 {Type: corekccv1alpha1.ContainerTypeOrganization},
1344 },
1345 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1346 hierarchicalRefProject,
1347 hierarchicalRefFolder,
1348 hierarchicalRefOrganization,
1349 },
1350 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1351 projectRefConfig,
1352 folderRefConfig,
1353 organizationRefConfig,
1354 },
1355 },
1356 refResourceReference: test.NewFolderUnstructured("folder-name", "folders/folder_id", corev1.ConditionTrue),
1357 expectedCanonName: "folders/folder_id/names/name",
1358 },
1359 {
1360 name: "template requires parent of multi-parent resource with external billing account reference that is set to a path",
1361 template: "{{parent}}/names/{{name}}",
1362 refResource: &k8s.Resource{
1363 TypeMeta: metav1.TypeMeta{
1364 APIVersion: "tfonly.cnrm.cloud.google.com/v1alpha1",
1365 Kind: "TFOnlyKind",
1366 },
1367 Spec: map[string]interface{}{
1368 "billingAccountRef": map[string]interface{}{
1369 "external": "billingAccounts/billing_account_id",
1370 },
1371 },
1372 },
1373 refResourceConfig: corekccv1alpha1.ResourceConfig{
1374 Name: "tf_only_kind",
1375 Kind: "TFOnlyKind",
1376 Containers: []corekccv1alpha1.Container{
1377 {Type: corekccv1alpha1.ContainerTypeProject},
1378 {Type: corekccv1alpha1.ContainerTypeFolder},
1379 {Type: corekccv1alpha1.ContainerTypeOrganization},
1380 },
1381 HierarchicalReferences: []corekccv1alpha1.HierarchicalReference{
1382 hierarchicalRefProject,
1383 hierarchicalRefFolder,
1384 hierarchicalRefOrganization,
1385 hierarchicalRefBillingAccount,
1386 },
1387 ResourceReferences: []corekccv1alpha1.ReferenceConfig{
1388 projectRefConfig,
1389 folderRefConfig,
1390 organizationRefConfig,
1391 billingAccountRefConfig,
1392 },
1393 },
1394 expectedCanonName: "billingAccounts/billing_account_id/names/name",
1395 },
1396 }
1397
1398 smLoader := testservicemetadataloader.NewForUnitTest()
1399 schemaLoader := testdclschemaloader.New(make(map[string]*openapi.Schema))
1400 for _, tc := range tests {
1401 tc := tc
1402 t.Run(tc.name, func(t *testing.T) {
1403 t.Parallel()
1404 testId := testvariable.NewUniqueId()
1405 c := mgr.GetClient()
1406 testcontroller.EnsureNamespaceExists(c, testId)
1407 tc.refResource.SetNamespace(testId)
1408 if tc.refResourceReference != nil {
1409 tc.refResourceReference.SetNamespace(testId)
1410 test.EnsureObjectExists(t, tc.refResourceReference, c)
1411 }
1412
1413
1414
1415
1416 serviceMapping := corekccv1alpha1.ServiceMapping{
1417 ObjectMeta: metav1.ObjectMeta{
1418 Namespace: "cnrm-system",
1419 Name: "tfonly.cnrm.cloud.google.com",
1420 },
1421 Spec: corekccv1alpha1.ServiceMappingSpec{
1422 Name: "tfonly",
1423 ServiceHostName: "tfonly",
1424 Version: "v1alpha1",
1425 Resources: []v1alpha1.ResourceConfig{
1426 tc.refResourceConfig,
1427 },
1428 },
1429 }
1430 serviceMappings := append(test.FakeServiceMappingsWithHierarchicalResources(), serviceMapping)
1431 serviceMappingLoader := servicemappingloader.NewFromServiceMappings(serviceMappings)
1432
1433 canonName, err := kcclite.CanonicalizeReferencedResourceName("name", tc.template, tc.refResource, smLoader, schemaLoader, serviceMappingLoader, c)
1434 if tc.errorCheckFunc != nil {
1435 tc.errorCheckFunc(t, err)
1436 return
1437 }
1438 if err != nil {
1439 t.Fatalf("got error, wanted none: %v", err)
1440 }
1441 if canonName != tc.expectedCanonName {
1442 t.Fatalf("got %v, want %v", canonName, tc.expectedCanonName)
1443 }
1444 })
1445 }
1446 }
1447
View as plain text