1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package conversion_test
16
17 import (
18 "reflect"
19 "testing"
20
21 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/constants"
22 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/conversion"
23 dclmetadata "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/metadata"
24 testdclschemaloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/dclschemaloader"
25 testservicemetadataloader "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/servicemetadataloader"
26
27 dclunstruct "github.com/GoogleCloudPlatform/declarative-resource-client-library/unstructured"
28 "github.com/google/go-cmp/cmp"
29 "github.com/nasa9084/go-openapi"
30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
31 )
32
33 var (
34 stv_foo = dclunstruct.ServiceTypeVersion{
35 Service: "test1",
36 Type: "Foo",
37 Version: "beta",
38 }
39 stv_bar = dclunstruct.ServiceTypeVersion{
40 Service: "test1",
41 Type: "Bar",
42 Version: "beta",
43 }
44 stv_baz = dclunstruct.ServiceTypeVersion{
45 Service: "test2",
46 Type: "Baz",
47 Version: "beta",
48 }
49 stv_noname = dclunstruct.ServiceTypeVersion{
50 Service: "test2",
51 Type: "NoName",
52 Version: "beta",
53 }
54 stv_nolabelsextension = dclunstruct.ServiceTypeVersion{
55 Service: "test1",
56 Type: "NoLabelsExtension",
57 Version: "beta",
58 }
59 )
60
61 func TestConverter_KRMObjectToDCLObject(t *testing.T) {
62 schemaLoader := testdclschemaloader.New(dclSchemaMap)
63 smLoader := testservicemetadataloader.NewForUnitTest()
64 converter := conversion.New(schemaLoader, smLoader)
65 kind := "Test1Foo"
66 apiVersion := "test1.cnrm.cloud.google.com/v1alpha1"
67 projectID := "dcl-test"
68 tests := []struct {
69 name string
70 krmObj *unstructured.Unstructured
71 dclObj *dclunstruct.Resource
72 hasError bool
73 }{
74 {
75 name: "name and project only",
76 krmObj: &unstructured.Unstructured{
77 Object: map[string]interface{}{
78 "kind": kind,
79 "apiVersion": apiVersion,
80 "metadata": map[string]interface{}{
81 "name": "foo-example",
82 "namespace": "test-system",
83 "annotations": map[string]interface{}{
84 "cnrm.cloud.google.com/project-id": projectID,
85 },
86 },
87 "spec": map[string]interface{}{
88 "resourceID": "CustomizedName",
89 },
90 },
91 },
92 dclObj: &dclunstruct.Resource{
93 STV: stv_foo,
94 Object: map[string]interface{}{
95 "name": "CustomizedName",
96 "project": projectID,
97 },
98 },
99 },
100 {
101 name: "default to metadata.name if user-specified name is not configured in spec",
102 krmObj: &unstructured.Unstructured{
103 Object: map[string]interface{}{
104 "kind": kind,
105 "apiVersion": apiVersion,
106 "metadata": map[string]interface{}{
107 "name": "foo-example",
108 "namespace": "test-system",
109 "annotations": map[string]interface{}{
110 "cnrm.cloud.google.com/project-id": projectID,
111 },
112 },
113 "spec": map[string]interface{}{},
114 },
115 },
116 dclObj: &dclunstruct.Resource{
117 STV: stv_foo,
118 Object: map[string]interface{}{
119 "name": "foo-example",
120 "project": projectID,
121 },
122 },
123 },
124 {
125 name: "error out if specified resourceID is an empty string",
126 krmObj: &unstructured.Unstructured{
127 Object: map[string]interface{}{
128 "kind": kind,
129 "apiVersion": apiVersion,
130 "metadata": map[string]interface{}{
131 "name": "foo-example",
132 "namespace": "test-system",
133 "annotations": map[string]interface{}{
134 "cnrm.cloud.google.com/project-id": projectID,
135 },
136 },
137 "spec": map[string]interface{}{
138 "resourceID": "",
139 },
140 },
141 },
142 hasError: true,
143 },
144 {
145 name: "server-generated id is configured for acquisition",
146 krmObj: &unstructured.Unstructured{
147 Object: map[string]interface{}{
148 "kind": "Test2Baz",
149 "apiVersion": "test2.cnrm.cloud.google.com/v1alpha1",
150 "metadata": map[string]interface{}{
151 "name": "foo-example",
152 "namespace": "test-system",
153 "annotations": map[string]interface{}{
154 "cnrm.cloud.google.com/project-id": projectID,
155 },
156 },
157 "spec": map[string]interface{}{
158 "resourceID": "server-generated-value",
159 },
160 },
161 },
162 dclObj: &dclunstruct.Resource{
163 STV: stv_baz,
164 Object: map[string]interface{}{
165 "name": "server-generated-value",
166 "project": projectID,
167 },
168 },
169 },
170 {
171 name: "server-generated id is not configured for creation",
172 krmObj: &unstructured.Unstructured{
173 Object: map[string]interface{}{
174 "kind": "Test2Baz",
175 "apiVersion": "test2.cnrm.cloud.google.com/v1alpha1",
176 "metadata": map[string]interface{}{
177 "name": "foo-example",
178 "namespace": "test-system",
179 "annotations": map[string]interface{}{
180 "cnrm.cloud.google.com/project-id": projectID,
181 },
182 },
183 "spec": map[string]interface{}{},
184 },
185 },
186 dclObj: &dclunstruct.Resource{
187 STV: stv_baz,
188 Object: map[string]interface{}{
189 "project": projectID,
190 },
191 },
192 },
193 {
194 name: "resource with no name field",
195 krmObj: &unstructured.Unstructured{
196 Object: map[string]interface{}{
197 "kind": "Test2NoName",
198 "apiVersion": "test2.cnrm.cloud.google.com/v1alpha1",
199 "metadata": map[string]interface{}{
200 "name": "foo-example",
201 "namespace": "test-system",
202 "annotations": map[string]interface{}{
203 "cnrm.cloud.google.com/project-id": projectID,
204 },
205 },
206 "spec": map[string]interface{}{
207 "resourceID": "should-be-ignored",
208 },
209 },
210 },
211 dclObj: &dclunstruct.Resource{
212 STV: stv_noname,
213 Object: map[string]interface{}{
214 "project": projectID,
215 },
216 },
217 },
218 {
219 name: "primitive fields",
220 krmObj: &unstructured.Unstructured{
221 Object: map[string]interface{}{
222 "kind": kind,
223 "apiVersion": apiVersion,
224 "metadata": map[string]interface{}{
225 "name": "foo-example",
226 "namespace": "test-system",
227 "annotations": map[string]interface{}{
228 "cnrm.cloud.google.com/project-id": projectID,
229 },
230 },
231 "spec": map[string]interface{}{
232 "intKey": 1,
233 "numberKey": 0.5,
234 "booleanKey": true,
235 "stringKey": "StringVal",
236 "resourceID": "CustomizedName",
237 },
238 },
239 },
240 dclObj: &dclunstruct.Resource{
241 STV: stv_foo,
242 Object: map[string]interface{}{
243 "intKey": int64(1),
244 "numberKey": float64(0.5),
245 "booleanKey": true,
246 "stringKey": "StringVal",
247 "name": "CustomizedName",
248 "project": projectID,
249 },
250 },
251 },
252 {
253 name: "numeric values canonicalization",
254 krmObj: &unstructured.Unstructured{
255 Object: map[string]interface{}{
256 "kind": kind,
257 "apiVersion": apiVersion,
258 "metadata": map[string]interface{}{
259 "name": "foo-example",
260 "namespace": "test-system",
261 "annotations": map[string]interface{}{
262 "cnrm.cloud.google.com/project-id": projectID,
263 },
264 },
265 "spec": map[string]interface{}{
266 "intKey": float64(1),
267 "numberKey": int64(1),
268 "resourceID": "CustomizedName",
269 },
270 },
271 },
272 dclObj: &dclunstruct.Resource{
273 STV: stv_foo,
274 Object: map[string]interface{}{
275 "intKey": int64(1),
276 "numberKey": float64(1),
277 "name": "CustomizedName",
278 "project": projectID,
279 },
280 },
281 },
282 {
283 name: "list of primitives",
284 krmObj: &unstructured.Unstructured{
285 Object: map[string]interface{}{
286 "kind": kind,
287 "apiVersion": apiVersion,
288 "metadata": map[string]interface{}{
289 "name": "foo-example",
290 "namespace": "test-system",
291 "annotations": map[string]interface{}{
292 "cnrm.cloud.google.com/project-id": projectID,
293 },
294 },
295 "spec": map[string]interface{}{
296 "primitiveArrayKey": []interface{}{
297 "myString1",
298 "myString2",
299 },
300 "resourceID": "foo-example",
301 },
302 },
303 },
304 dclObj: &dclunstruct.Resource{
305 STV: stv_foo,
306 Object: map[string]interface{}{
307 "primitiveArrayKey": []interface{}{
308 "myString1",
309 "myString2",
310 },
311 "name": "foo-example",
312 "project": projectID,
313 },
314 },
315 },
316 {
317 name: "nested object",
318 krmObj: &unstructured.Unstructured{
319 Object: map[string]interface{}{
320 "kind": kind,
321 "apiVersion": apiVersion,
322 "metadata": map[string]interface{}{
323 "name": "foo-example",
324 "namespace": "test-system",
325 "annotations": map[string]interface{}{
326 "cnrm.cloud.google.com/project-id": projectID,
327 },
328 },
329 "spec": map[string]interface{}{
330 "nestedObjectKey": map[string]interface{}{
331 "nestedField1": true,
332 "nestedField2": "StringVal",
333 "nestedReferenceFieldRef": map[string]interface{}{
334 "external": "reference-url",
335 },
336 "nestedSensitiveField": map[string]interface{}{
337 "value": "sensitive-data",
338 },
339 },
340 "resourceID": "foo-example",
341 },
342 },
343 },
344 dclObj: &dclunstruct.Resource{
345 STV: stv_foo,
346 Object: map[string]interface{}{
347 "nestedObjectKey": map[string]interface{}{
348 "nestedField1": true,
349 "nestedField2": "StringVal",
350 "nestedReferenceField": "reference-url",
351 "nestedSensitiveField": "sensitive-data",
352 },
353 "name": "foo-example",
354 "project": projectID,
355 },
356 },
357 },
358 {
359 name: "string-object map",
360 krmObj: &unstructured.Unstructured{
361 Object: map[string]interface{}{
362 "kind": kind,
363 "apiVersion": apiVersion,
364 "metadata": map[string]interface{}{
365 "name": "foo-example",
366 "namespace": "test-system",
367 "annotations": map[string]interface{}{
368 "cnrm.cloud.google.com/project-id": projectID,
369 },
370 },
371 "spec": map[string]interface{}{
372 "stringObjectMapKey": map[string]interface{}{
373 "someKey": map[string]interface{}{
374 "objectField1": 1.0,
375 "objectReferenceArrayKey": []interface{}{
376 map[string]interface{}{
377 "external": "my-ref-1",
378 },
379 map[string]interface{}{
380 "external": "my-ref-2",
381 },
382 },
383 "readOnlyField": "val1",
384 "readOnlySensitiveField": "sensitiveVal",
385 "readOnlyReferenceField": "referenceVal",
386 },
387 "someOtherKey": map[string]interface{}{
388 "objectField1": 2.0,
389 "objectReferenceArrayKey": []interface{}{
390 map[string]interface{}{
391 "external": "my-ref-3",
392 },
393 map[string]interface{}{
394 "external": "my-ref-4",
395 },
396 },
397 "readOnlyField": "val2",
398 },
399 },
400 "resourceID": "foo-example",
401 },
402 },
403 },
404 dclObj: &dclunstruct.Resource{
405 STV: stv_foo,
406 Object: map[string]interface{}{
407 "stringObjectMapKey": map[string]interface{}{
408 "someKey": map[string]interface{}{
409 "objectField1": 1.0,
410 "objectReferenceArrayKey": []interface{}{
411 "my-ref-1",
412 "my-ref-2",
413 },
414
415
416 "readOnlyField": "val1",
417
418
419
420
421 "readOnlySensitiveField": "sensitiveVal",
422 "readOnlyReferenceField": "referenceVal",
423 },
424 "someOtherKey": map[string]interface{}{
425 "objectField1": 2.0,
426 "objectReferenceArrayKey": []interface{}{
427 "my-ref-3",
428 "my-ref-4",
429 },
430 "readOnlyField": "val2",
431 },
432 },
433 "name": "foo-example",
434 "project": projectID,
435 },
436 },
437 },
438 {
439 name: "list of objects",
440 krmObj: &unstructured.Unstructured{
441 Object: map[string]interface{}{
442 "kind": kind,
443 "apiVersion": apiVersion,
444 "metadata": map[string]interface{}{
445 "name": "foo-example",
446 "namespace": "test-system",
447 "annotations": map[string]interface{}{
448 "cnrm.cloud.google.com/project-id": projectID,
449 },
450 },
451 "spec": map[string]interface{}{
452 "intKey": 1,
453 "numberKey": 0.5,
454 "booleanKey": true,
455 "stringKey": "StringVal",
456 "objectArrayKey": []interface{}{
457 map[string]interface{}{
458 "field1": 1,
459 "field2": "item1",
460 "readOnlyField": "oldVal1",
461 "readOnlySensitiveField": "sensitiveVal",
462 "readOnlyReferenceField": "referenceVal",
463 },
464 map[string]interface{}{
465 "field1": 2,
466 "field2": "item2",
467 "readOnlyField": "oldVal2",
468 },
469 },
470 "resourceID": "CustomizedName",
471 },
472 },
473 },
474 dclObj: &dclunstruct.Resource{
475 STV: stv_foo,
476 Object: map[string]interface{}{
477 "intKey": int64(1),
478 "numberKey": float64(0.5),
479 "booleanKey": true,
480 "stringKey": "StringVal",
481 "objectArrayKey": []interface{}{
482 map[string]interface{}{
483 "field1": float64(1),
484 "field2": "item1",
485
486
487 "readOnlyField": "oldVal1",
488
489
490
491
492 "readOnlySensitiveField": "sensitiveVal",
493 "readOnlyReferenceField": "referenceVal",
494 },
495 map[string]interface{}{
496 "field1": float64(2),
497 "field2": "item2",
498 "readOnlyField": "oldVal2",
499 },
500 },
501 "name": "CustomizedName",
502 "project": projectID,
503 },
504 },
505 },
506 {
507 name: "sensitive field",
508 krmObj: &unstructured.Unstructured{
509 Object: map[string]interface{}{
510 "kind": kind,
511 "apiVersion": apiVersion,
512 "metadata": map[string]interface{}{
513 "name": "foo-example",
514 "namespace": "test-system",
515 "annotations": map[string]interface{}{
516 "cnrm.cloud.google.com/project-id": projectID,
517 },
518 },
519 "spec": map[string]interface{}{
520 "sensitiveFieldKey": map[string]interface{}{
521 "value": "sensitive-data",
522 },
523 "resourceID": "CustomizedName",
524 },
525 },
526 },
527 dclObj: &dclunstruct.Resource{
528 STV: stv_foo,
529 Object: map[string]interface{}{
530 "sensitiveFieldKey": "sensitive-data",
531 "name": "CustomizedName",
532 "project": projectID,
533 },
534 },
535 },
536 {
537 name: "simple reference field",
538 krmObj: &unstructured.Unstructured{
539 Object: map[string]interface{}{
540 "kind": kind,
541 "apiVersion": apiVersion,
542 "metadata": map[string]interface{}{
543 "name": "foo-example",
544 "namespace": "test-system",
545 "annotations": map[string]interface{}{
546 "cnrm.cloud.google.com/project-id": projectID,
547 },
548 },
549 "spec": map[string]interface{}{
550 "referenceKeyRef": map[string]interface{}{
551 "external": "my-ref",
552 },
553 "resourceID": "CustomizedName",
554 },
555 },
556 },
557 dclObj: &dclunstruct.Resource{
558 STV: stv_foo,
559 Object: map[string]interface{}{
560 "referenceKey": "my-ref",
561 "name": "CustomizedName",
562 "project": projectID,
563 },
564 },
565 },
566 {
567 name: "complex reference field with bar resource kind",
568 krmObj: &unstructured.Unstructured{
569 Object: map[string]interface{}{
570 "kind": kind,
571 "apiVersion": apiVersion,
572 "metadata": map[string]interface{}{
573 "name": "foo-example",
574 "namespace": "test-system",
575 "annotations": map[string]interface{}{
576 "cnrm.cloud.google.com/project-id": projectID,
577 },
578 },
579 "spec": map[string]interface{}{
580 "complexReferenceKeyRef": map[string]interface{}{
581 "external": "bar-url",
582 },
583 "resourceID": "CustomizedName",
584 },
585 },
586 },
587 dclObj: &dclunstruct.Resource{
588 STV: stv_foo,
589 Object: map[string]interface{}{
590 "complexReferenceKey": "bar-url",
591 "name": "CustomizedName",
592 "project": projectID,
593 },
594 },
595 },
596 {
597 name: "complex reference field with baz resource kind",
598 krmObj: &unstructured.Unstructured{
599 Object: map[string]interface{}{
600 "kind": kind,
601 "apiVersion": apiVersion,
602 "metadata": map[string]interface{}{
603 "name": "foo-example",
604 "namespace": "test-system",
605 "annotations": map[string]interface{}{
606 "cnrm.cloud.google.com/project-id": projectID,
607 },
608 },
609 "spec": map[string]interface{}{
610 "complexReferenceKeyRef": map[string]interface{}{
611 "external": "baz-url",
612 },
613 "resourceID": "CustomizedName",
614 },
615 },
616 },
617 dclObj: &dclunstruct.Resource{
618 STV: stv_foo,
619 Object: map[string]interface{}{
620 "complexReferenceKey": "baz-url",
621 "name": "CustomizedName",
622 "project": projectID,
623 },
624 },
625 },
626 {
627 name: "a list of references",
628 krmObj: &unstructured.Unstructured{
629 Object: map[string]interface{}{
630 "kind": kind,
631 "apiVersion": apiVersion,
632 "metadata": map[string]interface{}{
633 "name": "foo-example",
634 "namespace": "test-system",
635 "annotations": map[string]interface{}{
636 "cnrm.cloud.google.com/project-id": projectID,
637 },
638 },
639 "spec": map[string]interface{}{
640 "referenceArrayKey": []interface{}{
641 map[string]interface{}{
642 "external": "my-ref-1",
643 },
644 map[string]interface{}{
645 "external": "my-ref-2",
646 },
647 },
648 "resourceID": "CustomizedName",
649 },
650 },
651 },
652 dclObj: &dclunstruct.Resource{
653 STV: stv_foo,
654 Object: map[string]interface{}{
655 "referenceArrayKey": []interface{}{
656 "my-ref-1",
657 "my-ref-2",
658 },
659 "name": "CustomizedName",
660 "project": projectID,
661 },
662 },
663 },
664 }
665 for _, tc := range tests {
666 tc := tc
667 t.Run(tc.name, func(t *testing.T) {
668 t.Parallel()
669 dclObj, err := converter.KRMObjectToDCLObject(tc.krmObj)
670 if tc.hasError {
671 if err == nil {
672 t.Fatalf("expected to get error when converting KRM to DCL")
673 }
674 return
675 }
676 if err != nil {
677 t.Fatalf("unexpected error: %v", err)
678 }
679 if !reflect.DeepEqual(dclObj, tc.dclObj) {
680 t.Fatalf("unexpected converted dcl obj diff (-want +got): \n%v", cmp.Diff(tc.dclObj, dclObj))
681 }
682 })
683 }
684 }
685
686 func TestConverter_KRMObjectToDCLObjectForHierarchicalReferences(t *testing.T) {
687 tests := []struct {
688 name string
689 schema *openapi.Schema
690 krmObj *unstructured.Unstructured
691 dclObj *dclunstruct.Resource
692 }{
693 {
694 name: "single-parent resource",
695 schema: &openapi.Schema{
696 Type: "object",
697 Properties: map[string]*openapi.Schema{
698 "project": &openapi.Schema{
699 Type: "string",
700 Extension: map[string]interface{}{
701 "x-dcl-references": []interface{}{
702 map[interface{}]interface{}{
703 "field": "name",
704 "parent": true,
705 "resource": "Cloudresourcemanager/Project",
706 },
707 },
708 },
709 },
710 },
711 },
712 krmObj: &unstructured.Unstructured{
713 Object: map[string]interface{}{
714 "kind": "Test5ProjectRef",
715 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
716 "metadata": map[string]interface{}{
717 "name": "name",
718 },
719 "spec": map[string]interface{}{
720 "projectRef": map[string]interface{}{
721 "external": "project_id",
722 },
723 },
724 },
725 },
726 dclObj: &dclunstruct.Resource{
727 STV: dclunstruct.ServiceTypeVersion{
728 Service: "test5",
729 Type: "ProjectRef",
730 Version: "beta",
731 },
732 Object: map[string]interface{}{
733 "project": "project_id",
734 },
735 },
736 },
737 {
738 name: "multi-parent resource with project reference",
739 schema: &openapi.Schema{
740 Type: "object",
741 Properties: map[string]*openapi.Schema{
742 "parent": &openapi.Schema{
743 Type: "string",
744 Extension: map[string]interface{}{
745 "x-dcl-references": []interface{}{
746 map[interface{}]interface{}{
747 "field": "name",
748 "parent": true,
749 "resource": "Cloudresourcemanager/Project",
750 },
751 map[interface{}]interface{}{
752 "field": "name",
753 "parent": true,
754 "resource": "Cloudresourcemanager/Folder",
755 },
756 map[interface{}]interface{}{
757 "field": "name",
758 "parent": true,
759 "resource": "Cloudresourcemanager/Organization",
760 },
761 map[interface{}]interface{}{
762 "field": "name",
763 "parent": true,
764 "resource": "Cloudresourcemanager/BillingAccount",
765 },
766 },
767 },
768 },
769 },
770 },
771 krmObj: &unstructured.Unstructured{
772 Object: map[string]interface{}{
773 "kind": "Test5MultipleRefs",
774 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
775 "metadata": map[string]interface{}{
776 "name": "name",
777 },
778 "spec": map[string]interface{}{
779 "projectRef": map[string]interface{}{
780 "external": "project_id",
781 },
782 },
783 },
784 },
785 dclObj: &dclunstruct.Resource{
786 STV: dclunstruct.ServiceTypeVersion{
787 Service: "test5",
788 Type: "MultipleRefs",
789 Version: "beta",
790 },
791 Object: map[string]interface{}{
792 "parent": "projects/project_id",
793 },
794 },
795 },
796 {
797 name: "multi-parent resource with folder reference",
798 schema: &openapi.Schema{
799 Type: "object",
800 Properties: map[string]*openapi.Schema{
801 "parent": &openapi.Schema{
802 Type: "string",
803 Extension: map[string]interface{}{
804 "x-dcl-references": []interface{}{
805 map[interface{}]interface{}{
806 "field": "name",
807 "parent": true,
808 "resource": "Cloudresourcemanager/Project",
809 },
810 map[interface{}]interface{}{
811 "field": "name",
812 "parent": true,
813 "resource": "Cloudresourcemanager/Folder",
814 },
815 map[interface{}]interface{}{
816 "field": "name",
817 "parent": true,
818 "resource": "Cloudresourcemanager/Organization",
819 },
820 map[interface{}]interface{}{
821 "field": "name",
822 "parent": true,
823 "resource": "Cloudresourcemanager/BillingAccount",
824 },
825 },
826 },
827 },
828 },
829 },
830 krmObj: &unstructured.Unstructured{
831 Object: map[string]interface{}{
832 "kind": "Test5MultipleRefs",
833 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
834 "metadata": map[string]interface{}{
835 "name": "name",
836 },
837 "spec": map[string]interface{}{
838 "folderRef": map[string]interface{}{
839 "external": "folder_id",
840 },
841 },
842 },
843 },
844 dclObj: &dclunstruct.Resource{
845 STV: dclunstruct.ServiceTypeVersion{
846 Service: "test5",
847 Type: "MultipleRefs",
848 Version: "beta",
849 },
850 Object: map[string]interface{}{
851 "parent": "folders/folder_id",
852 },
853 },
854 },
855 {
856 name: "multi-parent resource with organization reference",
857 schema: &openapi.Schema{
858 Type: "object",
859 Properties: map[string]*openapi.Schema{
860 "parent": &openapi.Schema{
861 Type: "string",
862 Extension: map[string]interface{}{
863 "x-dcl-references": []interface{}{
864 map[interface{}]interface{}{
865 "field": "name",
866 "parent": true,
867 "resource": "Cloudresourcemanager/Project",
868 },
869 map[interface{}]interface{}{
870 "field": "name",
871 "parent": true,
872 "resource": "Cloudresourcemanager/Folder",
873 },
874 map[interface{}]interface{}{
875 "field": "name",
876 "parent": true,
877 "resource": "Cloudresourcemanager/Organization",
878 },
879 map[interface{}]interface{}{
880 "field": "name",
881 "parent": true,
882 "resource": "Cloudresourcemanager/BillingAccount",
883 },
884 },
885 },
886 },
887 },
888 },
889 krmObj: &unstructured.Unstructured{
890 Object: map[string]interface{}{
891 "kind": "Test5MultipleRefs",
892 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
893 "metadata": map[string]interface{}{
894 "name": "name",
895 },
896 "spec": map[string]interface{}{
897 "organizationRef": map[string]interface{}{
898 "external": "organization_id",
899 },
900 },
901 },
902 },
903 dclObj: &dclunstruct.Resource{
904 STV: dclunstruct.ServiceTypeVersion{
905 Service: "test5",
906 Type: "MultipleRefs",
907 Version: "beta",
908 },
909 Object: map[string]interface{}{
910 "parent": "organizations/organization_id",
911 },
912 },
913 },
914 {
915 name: "multi-parent resource with billing account reference",
916 schema: &openapi.Schema{
917 Type: "object",
918 Properties: map[string]*openapi.Schema{
919 "parent": &openapi.Schema{
920 Type: "string",
921 Extension: map[string]interface{}{
922 "x-dcl-references": []interface{}{
923 map[interface{}]interface{}{
924 "field": "name",
925 "parent": true,
926 "resource": "Cloudresourcemanager/Project",
927 },
928 map[interface{}]interface{}{
929 "field": "name",
930 "parent": true,
931 "resource": "Cloudresourcemanager/Folder",
932 },
933 map[interface{}]interface{}{
934 "field": "name",
935 "parent": true,
936 "resource": "Cloudresourcemanager/Organization",
937 },
938 map[interface{}]interface{}{
939 "field": "name",
940 "parent": true,
941 "resource": "Cloudresourcemanager/BillingAccount",
942 },
943 },
944 },
945 },
946 },
947 },
948 krmObj: &unstructured.Unstructured{
949 Object: map[string]interface{}{
950 "kind": "Test5MultipleRefs",
951 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
952 "metadata": map[string]interface{}{
953 "name": "name",
954 },
955 "spec": map[string]interface{}{
956 "billingAccountRef": map[string]interface{}{
957 "external": "billing_account_id",
958 },
959 },
960 },
961 },
962 dclObj: &dclunstruct.Resource{
963 STV: dclunstruct.ServiceTypeVersion{
964 Service: "test5",
965 Type: "MultipleRefs",
966 Version: "beta",
967 },
968 Object: map[string]interface{}{
969 "parent": "billingAccounts/billing_account_id",
970 },
971 },
972 },
973 {
974 name: "multi-parent resource with reference value that already has parent prefix",
975 schema: &openapi.Schema{
976 Type: "object",
977 Properties: map[string]*openapi.Schema{
978 "parent": &openapi.Schema{
979 Type: "string",
980 Extension: map[string]interface{}{
981 "x-dcl-references": []interface{}{
982 map[interface{}]interface{}{
983 "field": "name",
984 "parent": true,
985 "resource": "Cloudresourcemanager/Project",
986 },
987 map[interface{}]interface{}{
988 "field": "name",
989 "parent": true,
990 "resource": "Cloudresourcemanager/Folder",
991 },
992 map[interface{}]interface{}{
993 "field": "name",
994 "parent": true,
995 "resource": "Cloudresourcemanager/Organization",
996 },
997 },
998 },
999 },
1000 },
1001 },
1002 krmObj: &unstructured.Unstructured{
1003 Object: map[string]interface{}{
1004 "kind": "Test5MultipleRefs",
1005 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1006 "metadata": map[string]interface{}{
1007 "name": "name",
1008 },
1009 "spec": map[string]interface{}{
1010 "projectRef": map[string]interface{}{
1011 "external": "projects/project_id",
1012 },
1013 },
1014 },
1015 },
1016 dclObj: &dclunstruct.Resource{
1017 STV: dclunstruct.ServiceTypeVersion{
1018 Service: "test5",
1019 Type: "MultipleRefs",
1020 Version: "beta",
1021 },
1022 Object: map[string]interface{}{
1023 "parent": "projects/project_id",
1024 },
1025 },
1026 },
1027 }
1028
1029 smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
1030 for _, tc := range tests {
1031 tc := tc
1032 t.Run(tc.name, func(t *testing.T) {
1033 t.Parallel()
1034 schemaKey := testdclschemaloader.DCLSchemaKeyForGVK(t, tc.krmObj.GroupVersionKind(), smLoader)
1035 schemaMap := map[string]*openapi.Schema{
1036 schemaKey: tc.schema,
1037 }
1038 schemaLoader := testdclschemaloader.New(schemaMap)
1039 converter := conversion.New(schemaLoader, smLoader)
1040
1041 dclObj, err := converter.KRMObjectToDCLObject(tc.krmObj)
1042 if err != nil {
1043 t.Fatalf("got error, but wanted none: %v", err)
1044 }
1045 if !reflect.DeepEqual(dclObj, tc.dclObj) {
1046 t.Fatalf("unexpected converted dcl obj diff (-want +got): \n%v", cmp.Diff(tc.dclObj, dclObj))
1047 }
1048 })
1049 }
1050 }
1051
1052 func TestConverter_DCLObjectToKRMObject(t *testing.T) {
1053 schemaLoader := testdclschemaloader.New(dclSchemaMap)
1054 smLoader := testservicemetadataloader.NewForUnitTest()
1055 converter := conversion.New(schemaLoader, smLoader)
1056 kind := "Test1Foo"
1057 apiVersion := "test1.cnrm.cloud.google.com/v1alpha1"
1058 projectID := "dcl-test"
1059 tests := []struct {
1060 name string
1061 krmObj *unstructured.Unstructured
1062 dclObj *dclunstruct.Resource
1063 hasError bool
1064 }{
1065 {
1066 name: "name and project only",
1067 krmObj: &unstructured.Unstructured{
1068 Object: map[string]interface{}{
1069 "kind": kind,
1070 "apiVersion": apiVersion,
1071 "metadata": map[string]interface{}{
1072 "name": "foo-example",
1073 "namespace": "test-system",
1074 "annotations": map[string]interface{}{
1075 "cnrm.cloud.google.com/project-id": projectID,
1076 },
1077 },
1078 "spec": map[string]interface{}{
1079 "resourceID": "CustomizedName",
1080 },
1081 },
1082 },
1083 dclObj: &dclunstruct.Resource{
1084 STV: stv_foo,
1085 Object: map[string]interface{}{
1086 "name": "CustomizedName",
1087 "project": projectID,
1088 },
1089 },
1090 },
1091 {
1092 name: "server-generated id is returned by the service",
1093 krmObj: &unstructured.Unstructured{
1094 Object: map[string]interface{}{
1095 "kind": "Test2Baz",
1096 "apiVersion": "test2.cnrm.cloud.google.com/v1alpha1",
1097 "metadata": map[string]interface{}{
1098 "name": "foo-example",
1099 "namespace": "test-system",
1100 "annotations": map[string]interface{}{
1101 "cnrm.cloud.google.com/project-id": projectID,
1102 },
1103 },
1104 "spec": map[string]interface{}{
1105 "resourceID": "server-generated-value",
1106 },
1107 },
1108 },
1109 dclObj: &dclunstruct.Resource{
1110 STV: stv_baz,
1111 Object: map[string]interface{}{
1112 "name": "server-generated-value",
1113 "project": projectID,
1114 },
1115 },
1116 },
1117 {
1118 name: "error out if server-generated id is not returned",
1119 dclObj: &dclunstruct.Resource{
1120 STV: stv_baz,
1121 Object: map[string]interface{}{
1122 "project": projectID,
1123 },
1124 },
1125 hasError: true,
1126 },
1127 {
1128 name: "resource with no name field",
1129 krmObj: &unstructured.Unstructured{
1130 Object: map[string]interface{}{
1131 "kind": "Test2NoName",
1132 "apiVersion": "test2.cnrm.cloud.google.com/v1alpha1",
1133 "metadata": map[string]interface{}{
1134 "name": "foo-example",
1135 "namespace": "test-system",
1136 "annotations": map[string]interface{}{
1137 "cnrm.cloud.google.com/project-id": projectID,
1138 },
1139 },
1140 "spec": map[string]interface{}{},
1141 },
1142 },
1143 dclObj: &dclunstruct.Resource{
1144 STV: stv_noname,
1145 Object: map[string]interface{}{
1146 "project": projectID,
1147 },
1148 },
1149 },
1150 {
1151 name: "primitive fields",
1152 krmObj: &unstructured.Unstructured{
1153 Object: map[string]interface{}{
1154 "kind": kind,
1155 "apiVersion": apiVersion,
1156 "metadata": map[string]interface{}{
1157 "name": "foo-example",
1158 "namespace": "test-system",
1159 "annotations": map[string]interface{}{
1160 "cnrm.cloud.google.com/project-id": projectID,
1161 },
1162 },
1163 "spec": map[string]interface{}{
1164 "intKey": 1,
1165 "numberKey": 0.5,
1166 "booleanKey": true,
1167 "stringKey": "StringVal",
1168 "resourceID": "CustomizedName",
1169 },
1170 },
1171 },
1172 dclObj: &dclunstruct.Resource{
1173 STV: stv_foo,
1174 Object: map[string]interface{}{
1175 "intKey": 1,
1176 "numberKey": 0.5,
1177 "booleanKey": true,
1178 "stringKey": "StringVal",
1179 "name": "CustomizedName",
1180 "project": projectID,
1181 },
1182 },
1183 },
1184 {
1185 name: "list of primitives",
1186 krmObj: &unstructured.Unstructured{
1187 Object: map[string]interface{}{
1188 "kind": kind,
1189 "apiVersion": apiVersion,
1190 "metadata": map[string]interface{}{
1191 "name": "foo-example",
1192 "namespace": "test-system",
1193 "annotations": map[string]interface{}{
1194 "cnrm.cloud.google.com/project-id": projectID,
1195 },
1196 },
1197 "spec": map[string]interface{}{
1198 "primitiveArrayKey": []interface{}{
1199 "myString1",
1200 "myString2",
1201 },
1202 "resourceID": "foo-example",
1203 },
1204 },
1205 },
1206 dclObj: &dclunstruct.Resource{
1207 STV: stv_foo,
1208 Object: map[string]interface{}{
1209 "primitiveArrayKey": []interface{}{
1210 "myString1",
1211 "myString2",
1212 },
1213 "name": "foo-example",
1214 "project": projectID,
1215 },
1216 },
1217 },
1218 {
1219 name: "nested object",
1220 krmObj: &unstructured.Unstructured{
1221 Object: map[string]interface{}{
1222 "kind": kind,
1223 "apiVersion": apiVersion,
1224 "metadata": map[string]interface{}{
1225 "name": "foo-example",
1226 "namespace": "test-system",
1227 "annotations": map[string]interface{}{
1228 "cnrm.cloud.google.com/project-id": projectID,
1229 },
1230 },
1231 "spec": map[string]interface{}{
1232 "nestedObjectKey": map[string]interface{}{
1233 "nestedField1": true,
1234 "nestedField2": "StringVal",
1235 "nestedReferenceFieldRef": map[string]interface{}{
1236 "external": "reference-url",
1237 },
1238 "nestedSensitiveField": map[string]interface{}{
1239 "value": "sensitive-data",
1240 },
1241 },
1242 "resourceID": "foo-example",
1243 },
1244 },
1245 },
1246 dclObj: &dclunstruct.Resource{
1247 STV: stv_foo,
1248 Object: map[string]interface{}{
1249 "nestedObjectKey": map[string]interface{}{
1250 "nestedField1": true,
1251 "nestedField2": "StringVal",
1252 "nestedReferenceField": "reference-url",
1253 "nestedSensitiveField": "sensitive-data",
1254 },
1255 "name": "foo-example",
1256 "project": projectID,
1257 },
1258 },
1259 },
1260 {
1261 name: "list of objects",
1262 krmObj: &unstructured.Unstructured{
1263 Object: map[string]interface{}{
1264 "kind": kind,
1265 "apiVersion": apiVersion,
1266 "metadata": map[string]interface{}{
1267 "name": "foo-example",
1268 "namespace": "test-system",
1269 "annotations": map[string]interface{}{
1270 "cnrm.cloud.google.com/project-id": projectID,
1271 },
1272 },
1273 "spec": map[string]interface{}{
1274 "intKey": 1,
1275 "numberKey": 0.5,
1276 "booleanKey": true,
1277 "stringKey": "StringVal",
1278 "objectArrayKey": []interface{}{
1279 map[string]interface{}{
1280 "field1": 1,
1281 "field2": "item1",
1282
1283
1284
1285 "readOnlyField": "running",
1286
1287
1288
1289
1290
1291 "readOnlySensitiveField": "sensitiveVal",
1292 "readOnlyReferenceField": "referenceVal",
1293 },
1294 map[string]interface{}{
1295 "field1": 2,
1296 "field2": "item2",
1297 "readOnlyField": "suspended",
1298 },
1299 },
1300 "resourceID": "CustomizedName",
1301 },
1302 },
1303 },
1304 dclObj: &dclunstruct.Resource{
1305 STV: stv_foo,
1306 Object: map[string]interface{}{
1307 "intKey": 1,
1308 "numberKey": 0.5,
1309 "booleanKey": true,
1310 "stringKey": "StringVal",
1311 "objectArrayKey": []interface{}{
1312 map[string]interface{}{
1313 "field1": 1,
1314 "field2": "item1",
1315 "readOnlyField": "running",
1316 "readOnlySensitiveField": "sensitiveVal",
1317 "readOnlyReferenceField": "referenceVal",
1318 },
1319 map[string]interface{}{
1320 "field1": 2,
1321 "field2": "item2",
1322 "readOnlyField": "suspended",
1323 },
1324 },
1325 "name": "CustomizedName",
1326 "project": projectID,
1327 },
1328 },
1329 },
1330 {
1331 name: "sensitive field",
1332 krmObj: &unstructured.Unstructured{
1333 Object: map[string]interface{}{
1334 "kind": kind,
1335 "apiVersion": apiVersion,
1336 "metadata": map[string]interface{}{
1337 "name": "foo-example",
1338 "namespace": "test-system",
1339 "annotations": map[string]interface{}{
1340 "cnrm.cloud.google.com/project-id": projectID,
1341 },
1342 },
1343 "spec": map[string]interface{}{
1344 "sensitiveFieldKey": map[string]interface{}{
1345 "value": "sensitive-data",
1346 },
1347 "resourceID": "CustomizedName",
1348 },
1349 },
1350 },
1351 dclObj: &dclunstruct.Resource{
1352 STV: stv_foo,
1353 Object: map[string]interface{}{
1354 "sensitiveFieldKey": "sensitive-data",
1355 "name": "CustomizedName",
1356 "project": projectID,
1357 },
1358 },
1359 },
1360 {
1361 name: "single-kind reference field",
1362 krmObj: &unstructured.Unstructured{
1363 Object: map[string]interface{}{
1364 "kind": kind,
1365 "apiVersion": apiVersion,
1366 "metadata": map[string]interface{}{
1367 "name": "foo-example",
1368 "namespace": "test-system",
1369 "annotations": map[string]interface{}{
1370 "cnrm.cloud.google.com/project-id": projectID,
1371 },
1372 },
1373 "spec": map[string]interface{}{
1374 "referenceKeyRef": map[string]interface{}{
1375 "external": "my-ref",
1376 },
1377 "resourceID": "CustomizedName",
1378 },
1379 },
1380 },
1381 dclObj: &dclunstruct.Resource{
1382 STV: stv_foo,
1383 Object: map[string]interface{}{
1384 "referenceKey": "my-ref",
1385 "name": "CustomizedName",
1386 "project": projectID,
1387 },
1388 },
1389 },
1390 {
1391 name: "multi-kind reference field",
1392 krmObj: &unstructured.Unstructured{
1393 Object: map[string]interface{}{
1394 "kind": kind,
1395 "apiVersion": apiVersion,
1396 "metadata": map[string]interface{}{
1397 "name": "foo-example",
1398 "namespace": "test-system",
1399 "annotations": map[string]interface{}{
1400 "cnrm.cloud.google.com/project-id": projectID,
1401 },
1402 },
1403 "spec": map[string]interface{}{
1404 "complexReferenceKeyRef": map[string]interface{}{
1405 "external": "my-ref",
1406 },
1407 "resourceID": "CustomizedName",
1408 },
1409 },
1410 },
1411 dclObj: &dclunstruct.Resource{
1412 STV: stv_foo,
1413 Object: map[string]interface{}{
1414 "complexReferenceKey": "my-ref",
1415 "name": "CustomizedName",
1416 "project": projectID,
1417 },
1418 },
1419 },
1420 {
1421 name: "a list of references",
1422 krmObj: &unstructured.Unstructured{
1423 Object: map[string]interface{}{
1424 "kind": kind,
1425 "apiVersion": apiVersion,
1426 "metadata": map[string]interface{}{
1427 "name": "foo-example",
1428 "namespace": "test-system",
1429 "annotations": map[string]interface{}{
1430 "cnrm.cloud.google.com/project-id": projectID,
1431 },
1432 },
1433 "spec": map[string]interface{}{
1434 "referenceArrayKey": []interface{}{
1435 map[string]interface{}{
1436 "external": "my-ref-1",
1437 },
1438 map[string]interface{}{
1439 "external": "my-ref-2",
1440 },
1441 },
1442 "resourceID": "CustomizedName",
1443 },
1444 },
1445 },
1446 dclObj: &dclunstruct.Resource{
1447 STV: stv_foo,
1448 Object: map[string]interface{}{
1449 "referenceArrayKey": []interface{}{
1450 "my-ref-1",
1451 "my-ref-2",
1452 },
1453 "name": "CustomizedName",
1454 "project": projectID,
1455 },
1456 },
1457 },
1458 {
1459 name: "string-object map",
1460 krmObj: &unstructured.Unstructured{
1461 Object: map[string]interface{}{
1462 "kind": kind,
1463 "apiVersion": apiVersion,
1464 "metadata": map[string]interface{}{
1465 "name": "foo-example",
1466 "namespace": "test-system",
1467 "annotations": map[string]interface{}{
1468 "cnrm.cloud.google.com/project-id": projectID,
1469 },
1470 },
1471 "spec": map[string]interface{}{
1472 "stringObjectMapKey": map[string]interface{}{
1473 "someKey": map[string]interface{}{
1474 "objectField1": 1.0,
1475 "objectReferenceArrayKey": []interface{}{
1476 map[string]interface{}{
1477 "external": "my-ref-1",
1478 },
1479 map[string]interface{}{
1480 "external": "my-ref-2",
1481 },
1482 },
1483
1484
1485
1486 "readOnlyField": "running",
1487
1488
1489
1490
1491
1492 "readOnlySensitiveField": "sensitiveVal",
1493 "readOnlyReferenceField": "referenceVal",
1494 },
1495 "someOtherKey": map[string]interface{}{
1496 "objectField1": 2.0,
1497 "objectReferenceArrayKey": []interface{}{
1498 map[string]interface{}{
1499 "external": "my-ref-3",
1500 },
1501 map[string]interface{}{
1502 "external": "my-ref-4",
1503 },
1504 },
1505 "readOnlyField": "val2",
1506 },
1507 },
1508 "resourceID": "foo-example",
1509 },
1510 },
1511 },
1512 dclObj: &dclunstruct.Resource{
1513 STV: stv_foo,
1514 Object: map[string]interface{}{
1515 "stringObjectMapKey": map[string]interface{}{
1516 "someKey": map[string]interface{}{
1517 "objectField1": 1.0,
1518 "objectReferenceArrayKey": []interface{}{
1519 "my-ref-1",
1520 "my-ref-2",
1521 },
1522 "readOnlyField": "running",
1523 "readOnlySensitiveField": "sensitiveVal",
1524 "readOnlyReferenceField": "referenceVal",
1525 },
1526 "someOtherKey": map[string]interface{}{
1527 "objectField1": 2.0,
1528 "objectReferenceArrayKey": []interface{}{
1529 "my-ref-3",
1530 "my-ref-4",
1531 },
1532 "readOnlyField": "val2",
1533 },
1534 },
1535 "name": "foo-example",
1536 "project": projectID,
1537 },
1538 },
1539 },
1540 }
1541 for _, tc := range tests {
1542 tc := tc
1543 t.Run(tc.name, func(t *testing.T) {
1544 t.Parallel()
1545 krmObj, err := converter.DCLObjectToKRMObject(tc.dclObj)
1546 if tc.hasError {
1547 if err == nil {
1548 t.Fatalf("expected to get error when converting DCL TO KRM")
1549 }
1550 return
1551 }
1552 if err != nil {
1553 t.Fatalf("unexpected error: %v", err)
1554 }
1555 compareUnstructuredObjects(t, krmObj, tc.krmObj)
1556 })
1557 }
1558 }
1559
1560 func TestConverter_DCLObjectToKRMObjectForHierarchicalReferences(t *testing.T) {
1561 tests := []struct {
1562 name string
1563 schema *openapi.Schema
1564 dclObj *dclunstruct.Resource
1565 krmObj *unstructured.Unstructured
1566 hasError bool
1567 }{
1568 {
1569 name: "single-parent resource",
1570 schema: &openapi.Schema{
1571 Type: "object",
1572 Properties: map[string]*openapi.Schema{
1573 "project": &openapi.Schema{
1574 Type: "string",
1575 Extension: map[string]interface{}{
1576 "x-dcl-references": []interface{}{
1577 map[interface{}]interface{}{
1578 "field": "name",
1579 "parent": true,
1580 "resource": "Cloudresourcemanager/Project",
1581 },
1582 },
1583 },
1584 },
1585 },
1586 },
1587 dclObj: &dclunstruct.Resource{
1588 STV: dclunstruct.ServiceTypeVersion{
1589 Service: "test5",
1590 Type: "ProjectRef",
1591 Version: "beta",
1592 },
1593 Object: map[string]interface{}{
1594 "project": "project_id",
1595 },
1596 },
1597 krmObj: &unstructured.Unstructured{
1598 Object: map[string]interface{}{
1599 "kind": "Test5ProjectRef",
1600 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1601 "spec": map[string]interface{}{
1602 "projectRef": map[string]interface{}{
1603 "external": "project_id",
1604 },
1605 },
1606 },
1607 },
1608 },
1609 {
1610 name: "multi-parent resource with project parent",
1611 schema: &openapi.Schema{
1612 Type: "object",
1613 Properties: map[string]*openapi.Schema{
1614 "parent": &openapi.Schema{
1615 Type: "string",
1616 Extension: map[string]interface{}{
1617 "x-dcl-references": []interface{}{
1618 map[interface{}]interface{}{
1619 "field": "name",
1620 "parent": true,
1621 "resource": "Cloudresourcemanager/Project",
1622 },
1623 map[interface{}]interface{}{
1624 "field": "name",
1625 "parent": true,
1626 "resource": "Cloudresourcemanager/Folder",
1627 },
1628 map[interface{}]interface{}{
1629 "field": "name",
1630 "parent": true,
1631 "resource": "Cloudresourcemanager/Organization",
1632 },
1633 },
1634 },
1635 },
1636 },
1637 },
1638 dclObj: &dclunstruct.Resource{
1639 STV: dclunstruct.ServiceTypeVersion{
1640 Service: "test5",
1641 Type: "MultipleRefs",
1642 Version: "beta",
1643 },
1644 Object: map[string]interface{}{
1645 "parent": "projects/project_id",
1646 },
1647 },
1648 krmObj: &unstructured.Unstructured{
1649 Object: map[string]interface{}{
1650 "kind": "Test5MultipleRefs",
1651 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1652 "spec": map[string]interface{}{
1653 "projectRef": map[string]interface{}{
1654 "external": "projects/project_id",
1655 },
1656 },
1657 },
1658 },
1659 },
1660 {
1661 name: "multi-parent resource with folder parent",
1662 schema: &openapi.Schema{
1663 Type: "object",
1664 Properties: map[string]*openapi.Schema{
1665 "parent": &openapi.Schema{
1666 Type: "string",
1667 Extension: map[string]interface{}{
1668 "x-dcl-references": []interface{}{
1669 map[interface{}]interface{}{
1670 "field": "name",
1671 "parent": true,
1672 "resource": "Cloudresourcemanager/Project",
1673 },
1674 map[interface{}]interface{}{
1675 "field": "name",
1676 "parent": true,
1677 "resource": "Cloudresourcemanager/Folder",
1678 },
1679 map[interface{}]interface{}{
1680 "field": "name",
1681 "parent": true,
1682 "resource": "Cloudresourcemanager/Organization",
1683 },
1684 },
1685 },
1686 },
1687 },
1688 },
1689 dclObj: &dclunstruct.Resource{
1690 STV: dclunstruct.ServiceTypeVersion{
1691 Service: "test5",
1692 Type: "MultipleRefs",
1693 Version: "beta",
1694 },
1695 Object: map[string]interface{}{
1696 "parent": "folders/folder_id",
1697 },
1698 },
1699 krmObj: &unstructured.Unstructured{
1700 Object: map[string]interface{}{
1701 "kind": "Test5MultipleRefs",
1702 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1703 "spec": map[string]interface{}{
1704 "folderRef": map[string]interface{}{
1705 "external": "folders/folder_id",
1706 },
1707 },
1708 },
1709 },
1710 },
1711 {
1712 name: "multi-parent resource with organization parent",
1713 schema: &openapi.Schema{
1714 Type: "object",
1715 Properties: map[string]*openapi.Schema{
1716 "parent": &openapi.Schema{
1717 Type: "string",
1718 Extension: map[string]interface{}{
1719 "x-dcl-references": []interface{}{
1720 map[interface{}]interface{}{
1721 "field": "name",
1722 "parent": true,
1723 "resource": "Cloudresourcemanager/Project",
1724 },
1725 map[interface{}]interface{}{
1726 "field": "name",
1727 "parent": true,
1728 "resource": "Cloudresourcemanager/Folder",
1729 },
1730 map[interface{}]interface{}{
1731 "field": "name",
1732 "parent": true,
1733 "resource": "Cloudresourcemanager/Organization",
1734 },
1735 },
1736 },
1737 },
1738 },
1739 },
1740 dclObj: &dclunstruct.Resource{
1741 STV: dclunstruct.ServiceTypeVersion{
1742 Service: "test5",
1743 Type: "MultipleRefs",
1744 Version: "beta",
1745 },
1746 Object: map[string]interface{}{
1747 "parent": "organizations/organization_id",
1748 },
1749 },
1750 krmObj: &unstructured.Unstructured{
1751 Object: map[string]interface{}{
1752 "kind": "Test5MultipleRefs",
1753 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1754 "spec": map[string]interface{}{
1755 "organizationRef": map[string]interface{}{
1756 "external": "organizations/organization_id",
1757 },
1758 },
1759 },
1760 },
1761 },
1762 {
1763 name: "multi-parent resource with billing account parent",
1764 schema: &openapi.Schema{
1765 Type: "object",
1766 Properties: map[string]*openapi.Schema{
1767 "parent": &openapi.Schema{
1768 Type: "string",
1769 Extension: map[string]interface{}{
1770 "x-dcl-references": []interface{}{
1771 map[interface{}]interface{}{
1772 "field": "name",
1773 "parent": true,
1774 "resource": "Cloudresourcemanager/Project",
1775 },
1776 map[interface{}]interface{}{
1777 "field": "name",
1778 "parent": true,
1779 "resource": "Cloudresourcemanager/Folder",
1780 },
1781 map[interface{}]interface{}{
1782 "field": "name",
1783 "parent": true,
1784 "resource": "Cloudresourcemanager/Organization",
1785 },
1786 map[interface{}]interface{}{
1787 "field": "name",
1788 "parent": true,
1789 "resource": "Cloudresourcemanager/BillingAccount",
1790 },
1791 },
1792 },
1793 },
1794 },
1795 },
1796 dclObj: &dclunstruct.Resource{
1797 STV: dclunstruct.ServiceTypeVersion{
1798 Service: "test5",
1799 Type: "MultipleRefs",
1800 Version: "beta",
1801 },
1802 Object: map[string]interface{}{
1803 "parent": "billingAccounts/billing_account_id",
1804 },
1805 },
1806 krmObj: &unstructured.Unstructured{
1807 Object: map[string]interface{}{
1808 "kind": "Test5MultipleRefs",
1809 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1810 "spec": map[string]interface{}{
1811 "billingAccountRef": map[string]interface{}{
1812 "external": "billingAccounts/billing_account_id",
1813 },
1814 },
1815 },
1816 },
1817 },
1818 {
1819 name: "multi-parent resource with empty parent",
1820 schema: &openapi.Schema{
1821 Type: "object",
1822 Properties: map[string]*openapi.Schema{
1823 "parent": &openapi.Schema{
1824 Type: "string",
1825 Extension: map[string]interface{}{
1826 "x-dcl-references": []interface{}{
1827 map[interface{}]interface{}{
1828 "field": "name",
1829 "parent": true,
1830 "resource": "Cloudresourcemanager/Project",
1831 },
1832 map[interface{}]interface{}{
1833 "field": "name",
1834 "parent": true,
1835 "resource": "Cloudresourcemanager/Folder",
1836 },
1837 map[interface{}]interface{}{
1838 "field": "name",
1839 "parent": true,
1840 "resource": "Cloudresourcemanager/Organization",
1841 },
1842 },
1843 },
1844 },
1845 },
1846 },
1847 dclObj: &dclunstruct.Resource{
1848 STV: dclunstruct.ServiceTypeVersion{
1849 Service: "test5",
1850 Type: "MultipleRefs",
1851 Version: "beta",
1852 },
1853 Object: map[string]interface{}{
1854 "parent": "",
1855 },
1856 },
1857 krmObj: &unstructured.Unstructured{
1858 Object: map[string]interface{}{
1859 "kind": "Test5MultipleRefs",
1860 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1861 },
1862 },
1863 hasError: true,
1864 },
1865 {
1866 name: "multi-parent resource with unrecognized parent",
1867 schema: &openapi.Schema{
1868 Type: "object",
1869 Properties: map[string]*openapi.Schema{
1870 "parent": &openapi.Schema{
1871 Type: "string",
1872 Extension: map[string]interface{}{
1873 "x-dcl-references": []interface{}{
1874 map[interface{}]interface{}{
1875 "field": "name",
1876 "parent": true,
1877 "resource": "Cloudresourcemanager/Project",
1878 },
1879 map[interface{}]interface{}{
1880 "field": "name",
1881 "parent": true,
1882 "resource": "Cloudresourcemanager/Folder",
1883 },
1884 map[interface{}]interface{}{
1885 "field": "name",
1886 "parent": true,
1887 "resource": "Cloudresourcemanager/Organization",
1888 },
1889 },
1890 },
1891 },
1892 },
1893 },
1894 dclObj: &dclunstruct.Resource{
1895 STV: dclunstruct.ServiceTypeVersion{
1896 Service: "test5",
1897 Type: "MultipleRefs",
1898 Version: "beta",
1899 },
1900 Object: map[string]interface{}{
1901 "parent": "val_with_no_known_parent_prefix",
1902 },
1903 },
1904 krmObj: &unstructured.Unstructured{
1905 Object: map[string]interface{}{
1906 "kind": "Test5MultipleRefs",
1907 "apiVersion": "test5.cnrm.cloud.google.com/v1alpha1",
1908 },
1909 },
1910 hasError: true,
1911 },
1912 }
1913
1914 smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
1915 for _, tc := range tests {
1916 tc := tc
1917 t.Run(tc.name, func(t *testing.T) {
1918 t.Parallel()
1919 schemaKey := testdclschemaloader.DCLSchemaKeyForGVK(t, tc.krmObj.GroupVersionKind(), smLoader)
1920 schemaMap := map[string]*openapi.Schema{
1921 schemaKey: tc.schema,
1922 }
1923 schemaLoader := testdclschemaloader.New(schemaMap)
1924 converter := conversion.New(schemaLoader, smLoader)
1925
1926 krmObj, err := converter.DCLObjectToKRMObject(tc.dclObj)
1927 if tc.hasError {
1928 if err == nil {
1929 t.Fatalf("got no error, but wanted one")
1930 }
1931 return
1932 }
1933 if err != nil {
1934 t.Fatalf("got error, but wanted none: %v", err)
1935 }
1936 compareUnstructuredObjects(t, krmObj, tc.krmObj)
1937 })
1938 }
1939 }
1940
1941 func TestLabelsConversion(t *testing.T) {
1942 schemaLoader := testdclschemaloader.New(dclSchemaMap)
1943 smLoader := testservicemetadataloader.NewForUnitTest()
1944 converter := conversion.New(schemaLoader, smLoader)
1945 kind := "Test1Bar"
1946 apiVersion := "test1.cnrm.cloud.google.com/v1alpha1"
1947 projectID := "dcl-test"
1948 tests := []struct {
1949 name string
1950 krmObj *unstructured.Unstructured
1951 dclObj *dclunstruct.Resource
1952 }{
1953 {
1954 name: "system label 'managed-by-cnrm' is added",
1955 krmObj: &unstructured.Unstructured{
1956 Object: map[string]interface{}{
1957 "kind": kind,
1958 "apiVersion": apiVersion,
1959 "metadata": map[string]interface{}{
1960 "name": "foo-example",
1961 "labels": map[string]interface{}{
1962 "key1": "val1",
1963 "key2": "val2",
1964 },
1965 "namespace": "test-system",
1966 "annotations": map[string]interface{}{
1967 "cnrm.cloud.google.com/project-id": projectID,
1968 },
1969 },
1970 "spec": map[string]interface{}{
1971 "resourceID": "CustomizedName",
1972 },
1973 },
1974 },
1975 dclObj: &dclunstruct.Resource{
1976 STV: stv_bar,
1977 Object: map[string]interface{}{
1978 "labels": map[string]interface{}{
1979 "key1": "val1",
1980 "key2": "val2",
1981 "managed-by-cnrm": "true",
1982 },
1983 "project": projectID,
1984 "name": "CustomizedName",
1985 },
1986 },
1987 },
1988 {
1989 name: "k8s style labels are trimmed",
1990 krmObj: &unstructured.Unstructured{
1991 Object: map[string]interface{}{
1992 "kind": kind,
1993 "apiVersion": apiVersion,
1994 "metadata": map[string]interface{}{
1995 "name": "foo-example",
1996 "labels": map[string]interface{}{
1997 "key1": "val1",
1998 "key2": "val2",
1999 "k8s.test.io/some-key": "some-value",
2000 },
2001 "namespace": "test-system",
2002 "annotations": map[string]interface{}{
2003 "cnrm.cloud.google.com/project-id": projectID,
2004 },
2005 },
2006 "spec": map[string]interface{}{
2007 "resourceID": "CustomizedName",
2008 },
2009 },
2010 },
2011 dclObj: &dclunstruct.Resource{
2012 STV: stv_bar,
2013 Object: map[string]interface{}{
2014 "labels": map[string]interface{}{
2015 "key1": "val1",
2016 "key2": "val2",
2017 "managed-by-cnrm": "true",
2018 },
2019 "project": projectID,
2020 "name": "CustomizedName",
2021 },
2022 },
2023 },
2024 {
2025 name: "without labels extension metadata labels will not be converted to dcl labels field",
2026 krmObj: &unstructured.Unstructured{
2027 Object: map[string]interface{}{
2028 "kind": "Test1NoLabelsExtension",
2029 "apiVersion": apiVersion,
2030 "metadata": map[string]interface{}{
2031 "name": "foo-example",
2032 "labels": map[string]interface{}{
2033 "key1": "val1",
2034 "key2": "val2",
2035 "k8s.test.io/some-key": "some-value",
2036 },
2037 "namespace": "test-system",
2038 "annotations": map[string]interface{}{
2039 "cnrm.cloud.google.com/project-id": projectID,
2040 },
2041 },
2042 "spec": map[string]interface{}{
2043 "resourceID": "CustomizedName",
2044 },
2045 },
2046 },
2047 dclObj: &dclunstruct.Resource{
2048 STV: stv_nolabelsextension,
2049 Object: map[string]interface{}{
2050 "project": projectID,
2051 "name": "CustomizedName",
2052 },
2053 },
2054 },
2055 {
2056 name: "without labels extension dcl labels will not be converted to metadata labels",
2057 krmObj: &unstructured.Unstructured{
2058 Object: map[string]interface{}{
2059 "kind": "Test1NoLabelsExtension",
2060 "apiVersion": apiVersion,
2061 "metadata": map[string]interface{}{
2062 "name": "foo-example",
2063 "labels": map[string]interface{}{},
2064 "namespace": "test-system",
2065 "annotations": map[string]interface{}{
2066 "cnrm.cloud.google.com/project-id": projectID,
2067 },
2068 },
2069 "spec": map[string]interface{}{
2070 "resourceID": "CustomizedName",
2071 "labels": map[string]interface{}{
2072 "key1": "val1",
2073 "key2": "val2",
2074 },
2075 },
2076 },
2077 },
2078 dclObj: &dclunstruct.Resource{
2079 STV: stv_nolabelsextension,
2080 Object: map[string]interface{}{
2081 "project": projectID,
2082 "name": "CustomizedName",
2083 "labels": map[string]interface{}{
2084 "key1": "val1",
2085 "key2": "val2",
2086 },
2087 },
2088 },
2089 },
2090 }
2091 for _, tc := range tests {
2092 tc := tc
2093 t.Run(tc.name, func(t *testing.T) {
2094 t.Parallel()
2095 dclObj, err := converter.KRMObjectToDCLObject(tc.krmObj)
2096 if err != nil {
2097 t.Fatalf("unexpected error: %v", err)
2098 }
2099 if !reflect.DeepEqual(dclObj, tc.dclObj) {
2100 t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(tc.dclObj, dclObj))
2101 }
2102 krmObj, err := converter.DCLObjectToKRMObject(tc.dclObj)
2103 if err != nil {
2104 t.Fatalf("unexpected error: %v", err)
2105 }
2106 dclSchema, err := converter.SchemaLoader.GetDCLSchema(dclObj.STV)
2107 if err != nil {
2108 t.Fatalf("unexpected error: %v", err)
2109 }
2110 labelsField := dclSchema.Extension[constants.DCL_LABELS_FIELD]
2111 want := interface{}(nil)
2112 if labelsField != nil {
2113 want = tc.dclObj.Object[labelsField.(string)].(map[string]interface{})
2114 }
2115 got := krmObj.Object["metadata"].(map[string]interface{})["labels"]
2116 if !reflect.DeepEqual(got, want) {
2117 t.Fatalf("unexpected labels diff (-want +got): \n%v", cmp.Diff(want, got))
2118 }
2119 })
2120 }
2121 }
2122
2123 func TestConverter_DCLObjectToKRMObject_WithStatus(t *testing.T) {
2124 schemaLoader := testdclschemaloader.New(dclSchemaMap)
2125 smLoader := testservicemetadataloader.NewForUnitTest()
2126 converter := conversion.New(schemaLoader, smLoader)
2127 kind := "Test1Foo"
2128 apiVersion := "test1.cnrm.cloud.google.com/v1alpha1"
2129 projectID := "dcl-test"
2130 tests := []struct {
2131 name string
2132 krmObj *unstructured.Unstructured
2133 dclObj *dclunstruct.Resource
2134 }{
2135 {
2136 name: "primitive fields",
2137 krmObj: &unstructured.Unstructured{
2138 Object: map[string]interface{}{
2139 "kind": kind,
2140 "apiVersion": apiVersion,
2141 "metadata": map[string]interface{}{
2142 "annotations": map[string]interface{}{
2143 "cnrm.cloud.google.com/project-id": projectID,
2144 },
2145 },
2146 "spec": map[string]interface{}{
2147 "intKey": 1,
2148 "numberKey": 0.5,
2149 "booleanKey": true,
2150 "stringKey": "StringVal",
2151 "resourceID": "CustomizedName",
2152 },
2153 "status": map[string]interface{}{
2154 "statusField": "message",
2155 },
2156 },
2157 },
2158 dclObj: &dclunstruct.Resource{
2159 STV: stv_foo,
2160 Object: map[string]interface{}{
2161 "intKey": 1,
2162 "numberKey": 0.5,
2163 "booleanKey": true,
2164 "stringKey": "StringVal",
2165 "name": "CustomizedName",
2166 "project": projectID,
2167 "statusField": "message",
2168 },
2169 },
2170 },
2171 {
2172 name: "nested status fields",
2173 krmObj: &unstructured.Unstructured{
2174 Object: map[string]interface{}{
2175 "kind": kind,
2176 "apiVersion": apiVersion,
2177 "metadata": map[string]interface{}{
2178 "annotations": map[string]interface{}{
2179 "cnrm.cloud.google.com/project-id": projectID,
2180 },
2181 },
2182 "spec": map[string]interface{}{
2183 "intKey": 1,
2184 "numberKey": 0.5,
2185 "booleanKey": true,
2186 "stringKey": "StringVal",
2187 "resourceID": "CustomizedName",
2188 "nestedObjectKey": map[string]interface{}{
2189 "nestedField1": true,
2190 "nestedField2": "StringVal",
2191 },
2192 },
2193 "status": map[string]interface{}{
2194 "statusField": "message",
2195 "nestedObjectKey": map[string]interface{}{
2196 "nestedStatusField": "StringVal2",
2197 "nestedStatusArrayString": []interface{}{"status1, status2"},
2198 },
2199 },
2200 },
2201 },
2202 dclObj: &dclunstruct.Resource{
2203 STV: stv_foo,
2204 Object: map[string]interface{}{
2205 "intKey": 1,
2206 "numberKey": 0.5,
2207 "booleanKey": true,
2208 "stringKey": "StringVal",
2209 "name": "CustomizedName",
2210 "project": projectID,
2211 "statusField": "message",
2212 "nestedObjectKey": map[string]interface{}{
2213 "nestedField1": true,
2214 "nestedField2": "StringVal",
2215 "nestedStatusField": "StringVal2",
2216 "nestedStatusArrayString": []interface{}{"status1, status2"},
2217 },
2218 },
2219 },
2220 },
2221 {
2222 name: "status array of primitives",
2223 krmObj: &unstructured.Unstructured{
2224 Object: map[string]interface{}{
2225 "kind": kind,
2226 "apiVersion": apiVersion,
2227 "metadata": map[string]interface{}{
2228 "annotations": map[string]interface{}{
2229 "cnrm.cloud.google.com/project-id": projectID,
2230 },
2231 },
2232 "spec": map[string]interface{}{
2233 "intKey": 1,
2234 "numberKey": 0.5,
2235 "booleanKey": true,
2236 "stringKey": "StringVal",
2237 "resourceID": "CustomizedName",
2238 },
2239 "status": map[string]interface{}{
2240 "statusField": "message",
2241 "statusArrayPrimitive": []interface{}{"status1, status2"},
2242 },
2243 },
2244 },
2245 dclObj: &dclunstruct.Resource{
2246 STV: stv_foo,
2247 Object: map[string]interface{}{
2248 "intKey": 1,
2249 "numberKey": 0.5,
2250 "booleanKey": true,
2251 "stringKey": "StringVal",
2252 "name": "CustomizedName",
2253 "project": projectID,
2254 "statusField": "message",
2255 "statusArrayPrimitive": []interface{}{"status1, status2"},
2256 },
2257 },
2258 },
2259 {
2260 name: "status array of objects",
2261 krmObj: &unstructured.Unstructured{
2262 Object: map[string]interface{}{
2263 "kind": kind,
2264 "apiVersion": apiVersion,
2265 "metadata": map[string]interface{}{
2266 "annotations": map[string]interface{}{
2267 "cnrm.cloud.google.com/project-id": projectID,
2268 },
2269 },
2270 "spec": map[string]interface{}{
2271 "intKey": 1,
2272 "numberKey": 0.5,
2273 "booleanKey": true,
2274 "stringKey": "StringVal",
2275 "resourceID": "CustomizedName",
2276 },
2277 "status": map[string]interface{}{
2278 "statusField": "message",
2279 "statusArrayObject": []interface{}{
2280 map[string]interface{}{
2281 "nestedStatusField1": "status1Field1",
2282 "nestedStatusField2": "status1Field2",
2283 },
2284 map[string]interface{}{
2285 "nestedStatusField1": "status2Field1",
2286 "nestedStatusField2": "status2Field2",
2287 },
2288 },
2289 },
2290 },
2291 },
2292 dclObj: &dclunstruct.Resource{
2293 STV: stv_foo,
2294 Object: map[string]interface{}{
2295 "intKey": 1,
2296 "numberKey": 0.5,
2297 "booleanKey": true,
2298 "stringKey": "StringVal",
2299 "name": "CustomizedName",
2300 "project": projectID,
2301 "statusField": "message",
2302 "statusArrayObject": []interface{}{
2303 map[string]interface{}{
2304 "nestedStatusField1": "status1Field1",
2305 "nestedStatusField2": "status1Field2",
2306 },
2307 map[string]interface{}{
2308 "nestedStatusField1": "status2Field1",
2309 "nestedStatusField2": "status2Field2",
2310 },
2311 },
2312 },
2313 },
2314 },
2315 {
2316 name: "status string-object map",
2317 krmObj: &unstructured.Unstructured{
2318 Object: map[string]interface{}{
2319 "kind": kind,
2320 "apiVersion": apiVersion,
2321 "metadata": map[string]interface{}{
2322 "annotations": map[string]interface{}{
2323 "cnrm.cloud.google.com/project-id": projectID,
2324 },
2325 },
2326 "spec": map[string]interface{}{
2327 "intKey": 1,
2328 "numberKey": 0.5,
2329 "booleanKey": true,
2330 "stringKey": "StringVal",
2331 "resourceID": "CustomizedName",
2332 },
2333 "status": map[string]interface{}{
2334 "statusField": "message",
2335 "statusStringObjectMap": map[string]interface{}{
2336 "someKey": map[string]interface{}{
2337 "nestedStatusField1": true,
2338 "nestedStatusField2": "str1",
2339 },
2340 "someOtherKey": map[string]interface{}{
2341 "nestedStatusField1": false,
2342 "nestedStatusField2": "str2",
2343 },
2344 },
2345 },
2346 },
2347 },
2348 dclObj: &dclunstruct.Resource{
2349 STV: stv_foo,
2350 Object: map[string]interface{}{
2351 "intKey": 1,
2352 "numberKey": 0.5,
2353 "booleanKey": true,
2354 "stringKey": "StringVal",
2355 "name": "CustomizedName",
2356 "project": projectID,
2357 "statusField": "message",
2358 "statusStringObjectMap": map[string]interface{}{
2359 "someKey": map[string]interface{}{
2360 "nestedStatusField1": true,
2361 "nestedStatusField2": "str1",
2362 },
2363 "someOtherKey": map[string]interface{}{
2364 "nestedStatusField1": false,
2365 "nestedStatusField2": "str2",
2366 },
2367 },
2368 },
2369 },
2370 },
2371 {
2372 name: "status with fields that collide with reserved names",
2373 krmObj: &unstructured.Unstructured{
2374 Object: map[string]interface{}{
2375 "kind": kind,
2376 "apiVersion": apiVersion,
2377 "metadata": map[string]interface{}{
2378 "annotations": map[string]interface{}{
2379 "cnrm.cloud.google.com/project-id": projectID,
2380 },
2381 },
2382 "spec": map[string]interface{}{
2383 "intKey": 1,
2384 "numberKey": 0.5,
2385 "booleanKey": true,
2386 "stringKey": "StringVal",
2387 "resourceID": "CustomizedName",
2388 },
2389 "status": map[string]interface{}{
2390 "statusField": "message",
2391 "resourceConditions": "value",
2392 "resourceObservedGeneration": "value",
2393 },
2394 },
2395 },
2396 dclObj: &dclunstruct.Resource{
2397 STV: stv_foo,
2398 Object: map[string]interface{}{
2399 "intKey": 1,
2400 "numberKey": 0.5,
2401 "booleanKey": true,
2402 "stringKey": "StringVal",
2403 "name": "CustomizedName",
2404 "project": projectID,
2405 "statusField": "message",
2406 "conditions": "value",
2407 "observedGeneration": "value",
2408 },
2409 },
2410 },
2411 }
2412 for _, tc := range tests {
2413 tc := tc
2414 t.Run(tc.name, func(t *testing.T) {
2415 t.Parallel()
2416 krmObj, err := converter.DCLObjectToKRMObject(tc.dclObj)
2417 if err != nil {
2418 t.Fatalf("unexpected error: %v", err)
2419 }
2420 compareUnstructuredObjects(t, krmObj, tc.krmObj)
2421 })
2422 }
2423 }
2424
2425 func compareUnstructuredObjects(t *testing.T, actual, expected *unstructured.Unstructured) {
2426
2427 unstructured.RemoveNestedField(actual.Object, "metadata", "name")
2428 unstructured.RemoveNestedField(expected.Object, "metadata", "name")
2429 unstructured.RemoveNestedField(actual.Object, "metadata", "namespace")
2430 unstructured.RemoveNestedField(expected.Object, "metadata", "namespace")
2431 if !reflect.DeepEqual(actual, expected) {
2432 t.Fatalf("unexpected spec diff (-want +got): \n%v", cmp.Diff(expected, actual))
2433 }
2434 }
2435
2436 var dclSchemaMap = map[string]*openapi.Schema{
2437 "test1_beta_foo": &openapi.Schema{
2438 Type: "object",
2439 Properties: map[string]*openapi.Schema{
2440 "project": &openapi.Schema{
2441 Type: "string",
2442 },
2443 "name": &openapi.Schema{
2444 Type: "string",
2445 },
2446 "stringKey": {
2447 Type: "string",
2448 },
2449 "intKey": {
2450 Type: "integer",
2451 },
2452 "booleanKey": {
2453 Type: "boolean",
2454 },
2455 "numberKey": {
2456 Type: "number",
2457 },
2458 "statusField": {
2459 Type: "string",
2460 ReadOnly: true,
2461 },
2462 "statusArrayPrimitive": {
2463 Type: "array",
2464 ReadOnly: true,
2465 Items: &openapi.Schema{
2466 Type: "string",
2467 ReadOnly: true,
2468 },
2469 },
2470 "statusArrayObject": {
2471 Type: "array",
2472 ReadOnly: true,
2473 Items: &openapi.Schema{
2474 Type: "object",
2475 ReadOnly: true,
2476 Properties: map[string]*openapi.Schema{
2477 "nestedStatusField1": &openapi.Schema{
2478 Type: "boolean",
2479 },
2480 "nestedStatusField2": &openapi.Schema{
2481 Type: "string",
2482 },
2483 },
2484 },
2485 },
2486 "statusStringObjectMap": {
2487 Type: "object",
2488 ReadOnly: true,
2489 AdditionalProperties: &openapi.Schema{
2490 Type: "object",
2491 Properties: map[string]*openapi.Schema{
2492 "nestedStatusField1": &openapi.Schema{
2493 Type: "boolean",
2494 },
2495 "nestedStatusField2": &openapi.Schema{
2496 Type: "string",
2497 },
2498 },
2499 },
2500 },
2501 "nestedObjectKey": {
2502 Type: "object",
2503 Properties: map[string]*openapi.Schema{
2504 "nestedField1": {
2505 Type: "boolean",
2506 },
2507 "nestedField2": {
2508 Type: "string",
2509 },
2510 "nestedReferenceField": {
2511 Type: "string",
2512 Extension: map[string]interface{}{
2513 "x-dcl-references": []interface{}{
2514 map[interface{}]interface{}{
2515 "resource": "SomeService/SomeKind",
2516 "field": "selfLink",
2517 },
2518 },
2519 },
2520 },
2521 "nestedSensitiveField": {
2522 Type: "string",
2523 Extension: map[string]interface{}{
2524 "x-dcl-sensitive": true,
2525 },
2526 },
2527 "nestedStatusField": {
2528 Type: "string",
2529 ReadOnly: true,
2530 },
2531 "nestedStatusArrayString": {
2532 Type: "array",
2533 ReadOnly: true,
2534 Items: &openapi.Schema{
2535 Type: "string",
2536 ReadOnly: true,
2537 },
2538 },
2539 },
2540 Required: []string{"nestedField1"},
2541 },
2542 "sensitiveFieldKey": {
2543 Type: "string",
2544 Extension: map[string]interface{}{
2545 "x-dcl-sensitive": true,
2546 },
2547 },
2548 "referenceKey": {
2549 Type: "string",
2550 Extension: map[string]interface{}{
2551 "x-dcl-references": []interface{}{
2552 map[interface{}]interface{}{
2553 "resource": "FakeService/FakeKind",
2554 "field": "name",
2555 },
2556 },
2557 },
2558 },
2559 "complexReferenceKey": {
2560 Type: "string",
2561 Extension: map[string]interface{}{
2562 "x-dcl-references": []interface{}{
2563 map[interface{}]interface{}{
2564 "resource": "FakeService/Bar",
2565 "field": "self_link",
2566 },
2567 map[interface{}]interface{}{
2568 "resource": "FakeService/Baz",
2569 "field": "self_link",
2570 },
2571 },
2572 },
2573 },
2574 "primitiveArrayKey": {
2575 Type: "array",
2576 Items: &openapi.Schema{
2577 Type: "string",
2578 },
2579 },
2580 "objectArrayKey": {
2581 Type: "array",
2582 Items: &openapi.Schema{
2583 Type: "object",
2584 Properties: map[string]*openapi.Schema{
2585 "field1": {
2586 Type: "number",
2587 },
2588 "field2": {
2589 Type: "string",
2590 },
2591 "readOnlyField": {
2592 Type: "string",
2593 ReadOnly: true,
2594 },
2595 "readOnlySensitiveField": {
2596 Type: "string",
2597 ReadOnly: true,
2598 Extension: map[string]interface{}{
2599 "x-dcl-sensitive": true,
2600 },
2601 },
2602 "readOnlyReferenceField": {
2603 Type: "string",
2604 ReadOnly: true,
2605 Extension: map[string]interface{}{
2606 "x-dcl-references": []interface{}{
2607 map[interface{}]interface{}{
2608 "resource": "FakeService/FakeKind",
2609 "field": "name",
2610 },
2611 },
2612 },
2613 },
2614 },
2615 },
2616 },
2617 "referenceArrayKey": {
2618 Type: "array",
2619 Items: &openapi.Schema{
2620 Type: "string",
2621 Extension: map[string]interface{}{
2622 "x-dcl-references": []interface{}{
2623 map[interface{}]interface{}{
2624 "resource": "FakeService/FakeKind",
2625 "field": "name",
2626 },
2627 },
2628 },
2629 },
2630 },
2631 "stringObjectMapKey": {
2632 Type: "object",
2633 AdditionalProperties: &openapi.Schema{
2634 Type: "object",
2635 Properties: map[string]*openapi.Schema{
2636 "objectField1": {
2637 Type: "number",
2638 },
2639 "objectReferenceArrayKey": {
2640 Type: "array",
2641 Items: &openapi.Schema{
2642 Type: "string",
2643 Extension: map[string]interface{}{
2644 "x-dcl-references": []interface{}{
2645 map[interface{}]interface{}{
2646 "resource": "FakeService/FakeKind",
2647 "field": "name",
2648 },
2649 },
2650 },
2651 },
2652 },
2653 "readOnlyField": {
2654 ReadOnly: true,
2655 Type: "string",
2656 },
2657 "readOnlySensitiveField": {
2658 Type: "string",
2659 ReadOnly: true,
2660 Extension: map[string]interface{}{
2661 "x-dcl-sensitive": true,
2662 },
2663 },
2664 "readOnlyReferenceField": {
2665 Type: "string",
2666 ReadOnly: true,
2667 Extension: map[string]interface{}{
2668 "x-dcl-references": []interface{}{
2669 map[interface{}]interface{}{
2670 "resource": "FakeService/FakeKind",
2671 "field": "name",
2672 },
2673 },
2674 },
2675 },
2676 },
2677 },
2678 },
2679 "conditions": {
2680 Type: "string",
2681 ReadOnly: true,
2682 },
2683 "observedGeneration": {
2684 Type: "string",
2685 ReadOnly: true,
2686 },
2687 },
2688 Extension: map[string]interface{}{
2689 "x-dcl-parent-container": "project",
2690 },
2691 },
2692 "test1_beta_bar": &openapi.Schema{
2693 Type: "object",
2694 Properties: map[string]*openapi.Schema{
2695 "project": &openapi.Schema{
2696 Type: "string",
2697 },
2698 "name": &openapi.Schema{
2699 Type: "string",
2700 },
2701 "labels": &openapi.Schema{
2702 Type: "object",
2703 AdditionalProperties: &openapi.Schema{
2704 Type: "string",
2705 },
2706 },
2707 },
2708 Extension: map[string]interface{}{
2709 "x-dcl-parent-container": "project",
2710 "x-dcl-labels": "labels",
2711 },
2712 },
2713 "test2_beta_baz": &openapi.Schema{
2714 Type: "object",
2715 Properties: map[string]*openapi.Schema{
2716 "project": &openapi.Schema{
2717 Type: "string",
2718 },
2719 "name": &openapi.Schema{
2720 Type: "string",
2721 Extension: map[string]interface{}{
2722 "x-dcl-server-generated-parameter": true,
2723 },
2724 },
2725 },
2726 Extension: map[string]interface{}{
2727 "x-dcl-parent-container": "project",
2728 },
2729 },
2730 "test2_beta_noname": &openapi.Schema{
2731 Type: "object",
2732 Properties: map[string]*openapi.Schema{
2733 "project": &openapi.Schema{
2734 Type: "string",
2735 },
2736 },
2737 Extension: map[string]interface{}{
2738 "x-dcl-parent-container": "project",
2739 },
2740 },
2741 "test1_beta_nolabelsextension": &openapi.Schema{
2742 Type: "object",
2743 Properties: map[string]*openapi.Schema{
2744 "project": &openapi.Schema{
2745 Type: "string",
2746 },
2747 "name": &openapi.Schema{
2748 Type: "string",
2749 },
2750 "labels": &openapi.Schema{
2751 Type: "object",
2752 AdditionalProperties: &openapi.Schema{
2753 Type: "string",
2754 },
2755 },
2756 },
2757 Extension: map[string]interface{}{
2758 "x-dcl-parent-container": "project",
2759 },
2760 },
2761 }
2762
View as plain text