...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/krmtotf/managedfields_test.go

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

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

View as plain text