...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/references_test.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dcl_test
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl"
    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  	"github.com/google/go-cmp/cmp"
    28  	"github.com/nasa9084/go-openapi"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  )
    31  
    32  func TestToTypeConfig(t *testing.T) {
    33  	tests := []struct {
    34  		name                string
    35  		dclRefExtensionElem map[interface{}]interface{}
    36  		tc                  *corekccv1alpha1.TypeConfig
    37  		hasError            bool
    38  	}{
    39  		{
    40  			name: "non-parent referenced resource type",
    41  			dclRefExtensionElem: map[interface{}]interface{}{
    42  				"resource": "Test1/Foo",
    43  				"field":    "name",
    44  			},
    45  			tc: &corekccv1alpha1.TypeConfig{
    46  				TargetField: "name",
    47  				GVK: schema.GroupVersionKind{
    48  					Group:   "test1.cnrm.cloud.google.com",
    49  					Version: "v1alpha1",
    50  					Kind:    "Test1Foo",
    51  				},
    52  				Key: "fooRef",
    53  			},
    54  		},
    55  		{
    56  			name: "parent referenced resource type",
    57  			dclRefExtensionElem: map[interface{}]interface{}{
    58  				"resource": "Test1/Foo",
    59  				"field":    "name",
    60  				"parent":   true,
    61  			},
    62  			tc: &corekccv1alpha1.TypeConfig{
    63  				TargetField: "name",
    64  				GVK: schema.GroupVersionKind{
    65  					Group:   "test1.cnrm.cloud.google.com",
    66  					Version: "v1alpha1",
    67  					Kind:    "Test1Foo",
    68  				},
    69  				Key:    "fooRef",
    70  				Parent: true,
    71  			},
    72  		},
    73  		{
    74  			name: "the referenced resource type has an unrecognized service",
    75  			dclRefExtensionElem: map[interface{}]interface{}{
    76  				"resource": "SomeUnknownService/Foo",
    77  				"field":    "name",
    78  				"parent":   true,
    79  			},
    80  			hasError: true,
    81  		},
    82  		{
    83  			name: "required 'field' attribute is not defined",
    84  			dclRefExtensionElem: map[interface{}]interface{}{
    85  				"resource": "Test1/Foo",
    86  			},
    87  			hasError: true,
    88  		},
    89  		{
    90  			name: "required 'resource' attribute is not defined",
    91  			dclRefExtensionElem: map[interface{}]interface{}{
    92  				"field": "name",
    93  			},
    94  			hasError: true,
    95  		},
    96  	}
    97  	smLoader := testservicemetadataloader.NewForUnitTest()
    98  	for _, tc := range tests {
    99  		tc := tc
   100  		t.Run(tc.name, func(t *testing.T) {
   101  			t.Parallel()
   102  			res, err := dcl.ToTypeConfig(tc.dclRefExtensionElem, smLoader)
   103  			if tc.hasError {
   104  				if err == nil {
   105  					t.Fatalf("got nil, but expect to get an error on converting to TypeConifg")
   106  				}
   107  				return
   108  			}
   109  			if err != nil {
   110  				t.Fatalf("unexpected error: %v", err)
   111  			}
   112  			if !reflect.DeepEqual(tc.tc, res) {
   113  				t.Fatalf("unexpected TypeConfig diff (-want +got): \n%v", cmp.Diff(tc.tc, res))
   114  			}
   115  		})
   116  	}
   117  }
   118  
   119  func TestGetReferenceTypeConfigs(t *testing.T) {
   120  	tests := []struct {
   121  		name        string
   122  		schema      *openapi.Schema
   123  		typeConfigs []corekccv1alpha1.TypeConfig
   124  	}{
   125  		{
   126  			name: "one non-parent type config",
   127  			schema: &openapi.Schema{
   128  				Extension: map[string]interface{}{
   129  					"x-dcl-references": []interface{}{
   130  						map[interface{}]interface{}{
   131  							"resource": "Test1/Foo",
   132  							"field":    "name",
   133  						},
   134  					},
   135  				},
   136  			},
   137  			typeConfigs: []corekccv1alpha1.TypeConfig{
   138  				{
   139  					TargetField: "name",
   140  					GVK: schema.GroupVersionKind{
   141  						Group:   "test1.cnrm.cloud.google.com",
   142  						Version: "v1alpha1",
   143  						Kind:    "Test1Foo",
   144  					},
   145  					Key: "fooRef",
   146  				},
   147  			},
   148  		},
   149  		{
   150  			name: "one parent type config",
   151  			schema: &openapi.Schema{
   152  				Extension: map[string]interface{}{
   153  					"x-dcl-references": []interface{}{
   154  						map[interface{}]interface{}{
   155  							"resource": "Test1/Foo",
   156  							"field":    "name",
   157  							"parent":   true,
   158  						},
   159  					},
   160  				},
   161  			},
   162  			typeConfigs: []corekccv1alpha1.TypeConfig{
   163  				{
   164  					TargetField: "name",
   165  					GVK: schema.GroupVersionKind{
   166  						Group:   "test1.cnrm.cloud.google.com",
   167  						Version: "v1alpha1",
   168  						Kind:    "Test1Foo",
   169  					},
   170  					Key:    "fooRef",
   171  					Parent: true,
   172  				},
   173  			},
   174  		},
   175  		{
   176  			name: "multiple non-parent type configs",
   177  			schema: &openapi.Schema{
   178  				Extension: map[string]interface{}{
   179  					"x-dcl-references": []interface{}{
   180  						map[interface{}]interface{}{
   181  							"resource": "Test1/Foo",
   182  							"field":    "name",
   183  						},
   184  						map[interface{}]interface{}{
   185  							"resource": "Test2/Baz",
   186  							"field":    "id",
   187  						},
   188  					},
   189  				},
   190  			},
   191  			typeConfigs: []corekccv1alpha1.TypeConfig{
   192  				{
   193  					TargetField: "name",
   194  					GVK: schema.GroupVersionKind{
   195  						Group:   "test1.cnrm.cloud.google.com",
   196  						Version: "v1alpha1",
   197  						Kind:    "Test1Foo",
   198  					},
   199  					Key: "fooRef",
   200  				},
   201  				{
   202  					TargetField: "id",
   203  					GVK: schema.GroupVersionKind{
   204  						Group:   "test2.cnrm.cloud.google.com",
   205  						Version: "v1alpha1",
   206  						Kind:    "Test2Baz",
   207  					},
   208  					Key: "bazRef",
   209  				},
   210  			},
   211  		},
   212  	}
   213  
   214  	smLoader := testservicemetadataloader.NewForUnitTest()
   215  	for _, tc := range tests {
   216  		tc := tc
   217  		t.Run(tc.name, func(t *testing.T) {
   218  			t.Parallel()
   219  			res, err := dcl.GetReferenceTypeConfigs(tc.schema, smLoader)
   220  			if err != nil {
   221  				t.Fatalf("got unexpected error: %v", err)
   222  			}
   223  			if !reflect.DeepEqual(tc.typeConfigs, res) {
   224  				t.Fatalf("unexpected diff in result (-want +got): \n%v", cmp.Diff(tc.typeConfigs, res))
   225  			}
   226  		})
   227  	}
   228  }
   229  
   230  func TestGetHierarchicalReferencesForGVK(t *testing.T) {
   231  	tests := []struct {
   232  		name     string
   233  		gvk      schema.GroupVersionKind
   234  		expected []corekccv1alpha1.HierarchicalReference
   235  	}{
   236  		{
   237  			name: "resource with no hierarchical reference",
   238  			gvk: schema.GroupVersionKind{
   239  				Group:   "test5.cnrm.cloud.google.com",
   240  				Version: "v1alpha1",
   241  				Kind:    "Test5NoHierarchicalRef",
   242  			},
   243  			expected: nil,
   244  		},
   245  		{
   246  			name: "resource with project reference",
   247  			gvk: schema.GroupVersionKind{
   248  				Group:   "test5.cnrm.cloud.google.com",
   249  				Version: "v1alpha1",
   250  				Kind:    "Test5ProjectRef",
   251  			},
   252  			expected: []corekccv1alpha1.HierarchicalReference{
   253  				{
   254  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   255  					Key:  "projectRef",
   256  				},
   257  			},
   258  		},
   259  		{
   260  			name: "resource with folder reference",
   261  			gvk: schema.GroupVersionKind{
   262  				Group:   "test5.cnrm.cloud.google.com",
   263  				Version: "v1alpha1",
   264  				Kind:    "Test5FolderRef",
   265  			},
   266  			expected: []corekccv1alpha1.HierarchicalReference{
   267  				{
   268  					Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
   269  					Key:  "folderRef",
   270  				},
   271  			},
   272  		},
   273  		{
   274  			name: "resource with organization reference",
   275  			gvk: schema.GroupVersionKind{
   276  				Group:   "test5.cnrm.cloud.google.com",
   277  				Version: "v1alpha1",
   278  				Kind:    "Test5OrganizationRef",
   279  			},
   280  			expected: []corekccv1alpha1.HierarchicalReference{
   281  				{
   282  					Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
   283  					Key:  "organizationRef",
   284  				},
   285  			},
   286  		},
   287  		{
   288  			name: "resource with multiple references",
   289  			gvk: schema.GroupVersionKind{
   290  				Group:   "test5.cnrm.cloud.google.com",
   291  				Version: "v1alpha1",
   292  				Kind:    "Test5MultipleRefs",
   293  			},
   294  			expected: []corekccv1alpha1.HierarchicalReference{
   295  				{
   296  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   297  					Key:  "projectRef",
   298  				},
   299  				{
   300  					Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
   301  					Key:  "folderRef",
   302  				},
   303  				{
   304  					Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
   305  					Key:  "organizationRef",
   306  				},
   307  			},
   308  		},
   309  		{
   310  			name: "resource with multiple references, but only two",
   311  			gvk: schema.GroupVersionKind{
   312  				Group:   "test5.cnrm.cloud.google.com",
   313  				Version: "v1alpha1",
   314  				Kind:    "Test5TwoRefs",
   315  			},
   316  			expected: []corekccv1alpha1.HierarchicalReference{
   317  				{
   318  					Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
   319  					Key:  "folderRef",
   320  				},
   321  				{
   322  					Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
   323  					Key:  "organizationRef",
   324  				},
   325  			},
   326  		},
   327  	}
   328  
   329  	smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
   330  	dclSchemaLoader := testdclschemaloader.New(dclSchemaMap())
   331  	for _, tc := range tests {
   332  		tc := tc
   333  		t.Run(tc.name, func(t *testing.T) {
   334  			t.Parallel()
   335  			actual, err := dcl.GetHierarchicalReferencesForGVK(tc.gvk, smLoader, dclSchemaLoader)
   336  			if err != nil {
   337  				t.Fatalf("got unexpected error: %v", err)
   338  			}
   339  			if !reflect.DeepEqual(tc.expected, actual) {
   340  				t.Fatalf("unexpected diff in result (-want +got): \n%v", cmp.Diff(tc.expected, actual))
   341  			}
   342  		})
   343  	}
   344  }
   345  
   346  func TestGetHierarchicalReferenceConfigForMultiParentResource(t *testing.T) {
   347  	tests := []struct {
   348  		name      string
   349  		schema    *openapi.Schema
   350  		expected  []corekccv1alpha1.HierarchicalReference
   351  		shouldErr bool
   352  	}{
   353  		{
   354  			name: "no parent field",
   355  			schema: &openapi.Schema{
   356  				Properties: map[string]*openapi.Schema{
   357  					"field": {Type: "string"},
   358  				},
   359  			},
   360  			shouldErr: true,
   361  		},
   362  		{
   363  			name: "has project field, but no parent field",
   364  			schema: &openapi.Schema{
   365  				Properties: map[string]*openapi.Schema{
   366  					"field": {Type: "string"},
   367  					"project": {
   368  						Type: "string",
   369  						Extension: map[string]interface{}{
   370  							"x-dcl-references": []interface{}{
   371  								map[interface{}]interface{}{
   372  									"field":    "name",
   373  									"parent":   true,
   374  									"resource": "Cloudresourcemanager/Project",
   375  								},
   376  							},
   377  						},
   378  					},
   379  				},
   380  			},
   381  			shouldErr: true,
   382  		},
   383  		{
   384  			name: "has parent field",
   385  			schema: &openapi.Schema{
   386  				Properties: map[string]*openapi.Schema{
   387  					"field": {Type: "string"},
   388  					"parent": {
   389  						Type: "string",
   390  						Extension: map[string]interface{}{
   391  							"x-dcl-references": []interface{}{
   392  								map[interface{}]interface{}{
   393  									"field":    "name",
   394  									"parent":   true,
   395  									"resource": "Cloudresourcemanager/Project",
   396  								},
   397  								map[interface{}]interface{}{
   398  									"field":    "name",
   399  									"parent":   true,
   400  									"resource": "Cloudresourcemanager/Folder",
   401  								},
   402  								map[interface{}]interface{}{
   403  									"field":    "name",
   404  									"parent":   true,
   405  									"resource": "Cloudresourcemanager/Organization",
   406  								},
   407  							},
   408  						},
   409  					},
   410  				},
   411  			},
   412  			expected: []corekccv1alpha1.HierarchicalReference{
   413  				{
   414  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   415  					Key:  "projectRef",
   416  				},
   417  				{
   418  					Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
   419  					Key:  "folderRef",
   420  				},
   421  				{
   422  					Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
   423  					Key:  "organizationRef",
   424  				},
   425  			},
   426  		},
   427  		{
   428  			name: "has parent field that can reference only two types of resources",
   429  			schema: &openapi.Schema{
   430  				Properties: map[string]*openapi.Schema{
   431  					"field": {Type: "string"},
   432  					"parent": {
   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/Folder",
   440  								},
   441  								map[interface{}]interface{}{
   442  									"field":    "name",
   443  									"parent":   true,
   444  									"resource": "Cloudresourcemanager/Organization",
   445  								},
   446  							},
   447  						},
   448  					},
   449  				},
   450  			},
   451  			expected: []corekccv1alpha1.HierarchicalReference{
   452  				{
   453  					Type: corekccv1alpha1.HierarchicalReferenceTypeFolder,
   454  					Key:  "folderRef",
   455  				},
   456  				{
   457  					Type: corekccv1alpha1.HierarchicalReferenceTypeOrganization,
   458  					Key:  "organizationRef",
   459  				},
   460  			},
   461  		},
   462  	}
   463  
   464  	smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
   465  	for _, tc := range tests {
   466  		tc := tc
   467  		t.Run(tc.name, func(t *testing.T) {
   468  			t.Parallel()
   469  			actual, err := dcl.GetHierarchicalReferenceConfigForMultiParentResource(tc.schema, smLoader)
   470  			if tc.shouldErr {
   471  				if err == nil {
   472  					t.Fatalf("got no error, but wanted one")
   473  				}
   474  				return
   475  			}
   476  			if err != nil {
   477  				t.Fatalf("got unexpected error: %v", err)
   478  			}
   479  			if !reflect.DeepEqual(tc.expected, actual) {
   480  				t.Fatalf("unexpected diff in result (-want +got): \n%v", cmp.Diff(tc.expected, actual))
   481  			}
   482  		})
   483  	}
   484  }
   485  
   486  func TestGetHierarchicalRefFromConfigForMultiParentResource(t *testing.T) {
   487  	tests := []struct {
   488  		name               string
   489  		schema             *openapi.Schema
   490  		config             map[string]interface{}
   491  		expectedVal        interface{}
   492  		expectedTypeConfig *corekccv1alpha1.TypeConfig
   493  		hasError           bool
   494  	}{
   495  		{
   496  			name: "no hierarchical reference in config (empty config)",
   497  			schema: &openapi.Schema{
   498  				Type: "string",
   499  				Extension: map[string]interface{}{
   500  					"x-dcl-references": []interface{}{
   501  						map[interface{}]interface{}{
   502  							"field":    "name",
   503  							"parent":   true,
   504  							"resource": "Cloudresourcemanager/Project",
   505  						},
   506  						map[interface{}]interface{}{
   507  							"field":    "name",
   508  							"parent":   true,
   509  							"resource": "Cloudresourcemanager/Folder",
   510  						},
   511  						map[interface{}]interface{}{
   512  							"field":    "name",
   513  							"parent":   true,
   514  							"resource": "Cloudresourcemanager/Organization",
   515  						},
   516  					},
   517  				},
   518  			},
   519  			expectedVal:        nil,
   520  			expectedTypeConfig: nil,
   521  		},
   522  		{
   523  			name: "no hierarchical reference in config",
   524  			schema: &openapi.Schema{
   525  				Type: "string",
   526  				Extension: map[string]interface{}{
   527  					"x-dcl-references": []interface{}{
   528  						map[interface{}]interface{}{
   529  							"field":    "name",
   530  							"parent":   true,
   531  							"resource": "Cloudresourcemanager/Project",
   532  						},
   533  						map[interface{}]interface{}{
   534  							"field":    "name",
   535  							"parent":   true,
   536  							"resource": "Cloudresourcemanager/Folder",
   537  						},
   538  						map[interface{}]interface{}{
   539  							"field":    "name",
   540  							"parent":   true,
   541  							"resource": "Cloudresourcemanager/Organization",
   542  						},
   543  					},
   544  				},
   545  			},
   546  			config: map[string]interface{}{
   547  				"field": "val",
   548  			},
   549  			expectedVal:        nil,
   550  			expectedTypeConfig: nil,
   551  		},
   552  		{
   553  			name: "project reference in config",
   554  			schema: &openapi.Schema{
   555  				Type: "string",
   556  				Extension: map[string]interface{}{
   557  					"x-dcl-references": []interface{}{
   558  						map[interface{}]interface{}{
   559  							"field":    "name",
   560  							"parent":   true,
   561  							"resource": "Cloudresourcemanager/Project",
   562  						},
   563  						map[interface{}]interface{}{
   564  							"field":    "name",
   565  							"parent":   true,
   566  							"resource": "Cloudresourcemanager/Folder",
   567  						},
   568  						map[interface{}]interface{}{
   569  							"field":    "name",
   570  							"parent":   true,
   571  							"resource": "Cloudresourcemanager/Organization",
   572  						},
   573  					},
   574  				},
   575  			},
   576  			config: map[string]interface{}{
   577  				"projectRef": map[string]interface{}{
   578  					"name": "project",
   579  				},
   580  			},
   581  			expectedVal: map[string]interface{}{
   582  				"name": "project",
   583  			},
   584  			expectedTypeConfig: &corekccv1alpha1.TypeConfig{
   585  				TargetField: "name",
   586  				GVK: schema.GroupVersionKind{
   587  					Group:   "resourcemanager.cnrm.cloud.google.com",
   588  					Version: "v1beta1",
   589  					Kind:    "Project",
   590  				},
   591  				Key:    "projectRef",
   592  				Parent: true,
   593  			},
   594  		},
   595  		{
   596  			name: "external project reference in config",
   597  			schema: &openapi.Schema{
   598  				Type: "string",
   599  				Extension: map[string]interface{}{
   600  					"x-dcl-references": []interface{}{
   601  						map[interface{}]interface{}{
   602  							"field":    "name",
   603  							"parent":   true,
   604  							"resource": "Cloudresourcemanager/Project",
   605  						},
   606  						map[interface{}]interface{}{
   607  							"field":    "name",
   608  							"parent":   true,
   609  							"resource": "Cloudresourcemanager/Folder",
   610  						},
   611  						map[interface{}]interface{}{
   612  							"field":    "name",
   613  							"parent":   true,
   614  							"resource": "Cloudresourcemanager/Organization",
   615  						},
   616  					},
   617  				},
   618  			},
   619  			config: map[string]interface{}{
   620  				"projectRef": map[string]interface{}{
   621  					"external": "project",
   622  				},
   623  			},
   624  			expectedVal: map[string]interface{}{
   625  				"external": "project",
   626  			},
   627  			expectedTypeConfig: &corekccv1alpha1.TypeConfig{
   628  				TargetField: "name",
   629  				GVK: schema.GroupVersionKind{
   630  					Group:   "resourcemanager.cnrm.cloud.google.com",
   631  					Version: "v1beta1",
   632  					Kind:    "Project",
   633  				},
   634  				Key:    "projectRef",
   635  				Parent: true,
   636  			},
   637  		},
   638  		{
   639  			name: "namespaced project reference in config",
   640  			schema: &openapi.Schema{
   641  				Type: "string",
   642  				Extension: map[string]interface{}{
   643  					"x-dcl-references": []interface{}{
   644  						map[interface{}]interface{}{
   645  							"field":    "name",
   646  							"parent":   true,
   647  							"resource": "Cloudresourcemanager/Project",
   648  						},
   649  						map[interface{}]interface{}{
   650  							"field":    "name",
   651  							"parent":   true,
   652  							"resource": "Cloudresourcemanager/Folder",
   653  						},
   654  						map[interface{}]interface{}{
   655  							"field":    "name",
   656  							"parent":   true,
   657  							"resource": "Cloudresourcemanager/Organization",
   658  						},
   659  					},
   660  				},
   661  			},
   662  			config: map[string]interface{}{
   663  				"projectRef": map[string]interface{}{
   664  					"namespace": "project-namespace",
   665  					"name":      "project",
   666  				},
   667  			},
   668  			expectedVal: map[string]interface{}{
   669  				"namespace": "project-namespace",
   670  				"name":      "project",
   671  			},
   672  			expectedTypeConfig: &corekccv1alpha1.TypeConfig{
   673  				TargetField: "name",
   674  				GVK: schema.GroupVersionKind{
   675  					Group:   "resourcemanager.cnrm.cloud.google.com",
   676  					Version: "v1beta1",
   677  					Kind:    "Project",
   678  				},
   679  				Key:    "projectRef",
   680  				Parent: true,
   681  			},
   682  		},
   683  		{
   684  			name: "folder reference in config",
   685  			schema: &openapi.Schema{
   686  				Type: "string",
   687  				Extension: map[string]interface{}{
   688  					"x-dcl-references": []interface{}{
   689  						map[interface{}]interface{}{
   690  							"field":    "name",
   691  							"parent":   true,
   692  							"resource": "Cloudresourcemanager/Project",
   693  						},
   694  						map[interface{}]interface{}{
   695  							"field":    "name",
   696  							"parent":   true,
   697  							"resource": "Cloudresourcemanager/Folder",
   698  						},
   699  						map[interface{}]interface{}{
   700  							"field":    "name",
   701  							"parent":   true,
   702  							"resource": "Cloudresourcemanager/Organization",
   703  						},
   704  					},
   705  				},
   706  			},
   707  			config: map[string]interface{}{
   708  				"folderRef": map[string]interface{}{
   709  					"name": "folder",
   710  				},
   711  			},
   712  			expectedVal: map[string]interface{}{
   713  				"name": "folder",
   714  			},
   715  			expectedTypeConfig: &corekccv1alpha1.TypeConfig{
   716  				TargetField: "name",
   717  				GVK: schema.GroupVersionKind{
   718  					Group:   "resourcemanager.cnrm.cloud.google.com",
   719  					Version: "v1beta1",
   720  					Kind:    "Folder",
   721  				},
   722  				Key:    "folderRef",
   723  				Parent: true,
   724  			},
   725  		},
   726  		{
   727  			name: "organization reference in config",
   728  			schema: &openapi.Schema{
   729  				Type: "string",
   730  				Extension: map[string]interface{}{
   731  					"x-dcl-references": []interface{}{
   732  						map[interface{}]interface{}{
   733  							"field":    "name",
   734  							"parent":   true,
   735  							"resource": "Cloudresourcemanager/Project",
   736  						},
   737  						map[interface{}]interface{}{
   738  							"field":    "name",
   739  							"parent":   true,
   740  							"resource": "Cloudresourcemanager/Folder",
   741  						},
   742  						map[interface{}]interface{}{
   743  							"field":    "name",
   744  							"parent":   true,
   745  							"resource": "Cloudresourcemanager/Organization",
   746  						},
   747  					},
   748  				},
   749  			},
   750  			config: map[string]interface{}{
   751  				"organizationRef": map[string]interface{}{
   752  					"external": "organization",
   753  				},
   754  			},
   755  			expectedVal: map[string]interface{}{
   756  				"external": "organization",
   757  			},
   758  			expectedTypeConfig: &corekccv1alpha1.TypeConfig{
   759  				TargetField: "name",
   760  				GVK: schema.GroupVersionKind{
   761  					Group:   "resourcemanager.cnrm.cloud.google.com",
   762  					Version: "v1beta1",
   763  					Kind:    "Organization",
   764  				},
   765  				Key:    "organizationRef",
   766  				Parent: true,
   767  			},
   768  		},
   769  		{
   770  			name: "multiple hierarchical references in config",
   771  			schema: &openapi.Schema{
   772  				Type: "string",
   773  				Extension: map[string]interface{}{
   774  					"x-dcl-references": []interface{}{
   775  						map[interface{}]interface{}{
   776  							"field":    "name",
   777  							"parent":   true,
   778  							"resource": "Cloudresourcemanager/Project",
   779  						},
   780  						map[interface{}]interface{}{
   781  							"field":    "name",
   782  							"parent":   true,
   783  							"resource": "Cloudresourcemanager/Folder",
   784  						},
   785  						map[interface{}]interface{}{
   786  							"field":    "name",
   787  							"parent":   true,
   788  							"resource": "Cloudresourcemanager/Organization",
   789  						},
   790  					},
   791  				},
   792  			},
   793  			config: map[string]interface{}{
   794  				"folderRef": map[string]interface{}{
   795  					"external": "folder",
   796  				},
   797  				"organizationRef": map[string]interface{}{
   798  					"external": "organization",
   799  				},
   800  			},
   801  			hasError: true,
   802  		},
   803  	}
   804  
   805  	smLoader := dclmetadata.NewFromServiceList(testservicemetadataloader.FakeServiceMetadataWithHierarchicalResources())
   806  	for _, tc := range tests {
   807  		tc := tc
   808  		t.Run(tc.name, func(t *testing.T) {
   809  			t.Parallel()
   810  			val, typeConfig, err := dcl.GetHierarchicalRefFromConfigForMultiParentResource(tc.config, tc.schema, smLoader)
   811  			if tc.hasError {
   812  				if err == nil {
   813  					t.Fatalf("got no error, but wanted one")
   814  				}
   815  				return
   816  			}
   817  			if err != nil {
   818  				t.Fatalf("got unexpected error: %v", err)
   819  			}
   820  			if !reflect.DeepEqual(tc.expectedVal, val) {
   821  				t.Fatalf("unexpected diff in resulting val (-want +got): \n%v", cmp.Diff(tc.expectedVal, val))
   822  			}
   823  			if !reflect.DeepEqual(tc.expectedTypeConfig, typeConfig) {
   824  				t.Fatalf("unexpected diff in resulting type config (-want +got): \n%v", cmp.Diff(tc.expectedTypeConfig, typeConfig))
   825  			}
   826  		})
   827  	}
   828  }
   829  
   830  func dclSchemaMap() map[string]*openapi.Schema {
   831  	return map[string]*openapi.Schema{
   832  		"test5_beta_nohierarchicalref": &openapi.Schema{
   833  			Type: "object",
   834  			Properties: map[string]*openapi.Schema{
   835  				"name": &openapi.Schema{
   836  					Type: "string",
   837  				},
   838  			},
   839  		},
   840  		"test5_beta_projectref": &openapi.Schema{
   841  			Type: "object",
   842  			Properties: map[string]*openapi.Schema{
   843  				"project": &openapi.Schema{
   844  					Type: "string",
   845  					Extension: map[string]interface{}{
   846  						"x-dcl-references": []interface{}{
   847  							map[interface{}]interface{}{
   848  								"field":    "name",
   849  								"parent":   true,
   850  								"resource": "Cloudresourcemanager/Project",
   851  							},
   852  						},
   853  					},
   854  				},
   855  			},
   856  		},
   857  		"test5_beta_folderref": &openapi.Schema{
   858  			Type: "object",
   859  			Properties: map[string]*openapi.Schema{
   860  				"folder": &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/Folder",
   868  							},
   869  						},
   870  					},
   871  				},
   872  			},
   873  		},
   874  		"test5_beta_organizationref": &openapi.Schema{
   875  			Type: "object",
   876  			Properties: map[string]*openapi.Schema{
   877  				"organization": &openapi.Schema{
   878  					Type: "string",
   879  					Extension: map[string]interface{}{
   880  						"x-dcl-references": []interface{}{
   881  							map[interface{}]interface{}{
   882  								"field":    "name",
   883  								"parent":   true,
   884  								"resource": "Cloudresourcemanager/Organization",
   885  							},
   886  						},
   887  					},
   888  				},
   889  			},
   890  		},
   891  		"test5_beta_multiplerefs": &openapi.Schema{
   892  			Type: "object",
   893  			Properties: map[string]*openapi.Schema{
   894  				"parent": &openapi.Schema{
   895  					Type: "string",
   896  					Extension: map[string]interface{}{
   897  						"x-dcl-references": []interface{}{
   898  							map[interface{}]interface{}{
   899  								"field":    "name",
   900  								"parent":   true,
   901  								"resource": "Cloudresourcemanager/Project",
   902  							},
   903  							map[interface{}]interface{}{
   904  								"field":    "name",
   905  								"parent":   true,
   906  								"resource": "Cloudresourcemanager/Folder",
   907  							},
   908  							map[interface{}]interface{}{
   909  								"field":    "name",
   910  								"parent":   true,
   911  								"resource": "Cloudresourcemanager/Organization",
   912  							},
   913  						},
   914  					},
   915  				},
   916  			},
   917  		},
   918  		"test5_beta_tworefs": &openapi.Schema{
   919  			Type: "object",
   920  			Properties: map[string]*openapi.Schema{
   921  				"parent": &openapi.Schema{
   922  					Type: "string",
   923  					Extension: map[string]interface{}{
   924  						"x-dcl-references": []interface{}{
   925  							map[interface{}]interface{}{
   926  								"field":    "name",
   927  								"parent":   true,
   928  								"resource": "Cloudresourcemanager/Folder",
   929  							},
   930  							map[interface{}]interface{}{
   931  								"field":    "name",
   932  								"parent":   true,
   933  								"resource": "Cloudresourcemanager/Organization",
   934  							},
   935  						},
   936  					},
   937  				},
   938  			},
   939  		},
   940  
   941  		// Add the following to the list of fake DCL schemas to allow for our
   942  		// test to test resources that reference hierarchical resources
   943  		// (e.g. "Cloudresourcemanager/Project").
   944  		"cloudresourcemanager_ga_project": &openapi.Schema{},
   945  		"cloudresourcemanager_ga_folder":  &openapi.Schema{},
   946  	}
   947  }
   948  

View as plain text