...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/krmtotf/legacygcpmanagedfields_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  	"encoding/json"
    19  	"testing"
    20  
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    22  	. "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/krmtotf"
    23  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
    24  
    25  	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
    26  	"k8s.io/apimachinery/pkg/runtime/schema"
    27  )
    28  
    29  var emptyObject = make(map[string]interface{})
    30  
    31  func TestResolveGCPManagedFields(t *testing.T) {
    32  	tests := []struct {
    33  		name              string
    34  		kind              string
    35  		lastAppliedConfig map[string]interface{}
    36  		resourceExists    bool
    37  		inputConfig       map[string]interface{}
    38  		expectedConfig    map[string]interface{}
    39  	}{
    40  		{
    41  			name: "ContainerCluster treats node version as GCP-managed when release channel set",
    42  			kind: "ContainerCluster",
    43  			lastAppliedConfig: map[string]interface{}{
    44  				"spec": map[string]interface{}{
    45  					"otherField": "otherValue",
    46  					"releaseChannel": map[string]interface{}{
    47  						"channel": "REGULAR",
    48  					},
    49  				},
    50  			},
    51  			resourceExists: true,
    52  			inputConfig: map[string]interface{}{
    53  				"otherField": "otherValue",
    54  				"releaseChannel": map[string]interface{}{
    55  					"channel": "REGULAR",
    56  				},
    57  				"nodeVersion": "1.14",
    58  			},
    59  			expectedConfig: map[string]interface{}{
    60  				"otherField": "otherValue",
    61  				"releaseChannel": map[string]interface{}{
    62  					"channel": "REGULAR",
    63  				},
    64  			},
    65  		},
    66  		{
    67  			name: "SQLInstance treats disk size as GCP-managed when disk autoresize set",
    68  			kind: "SQLInstance",
    69  			lastAppliedConfig: map[string]interface{}{
    70  				"spec": map[string]interface{}{
    71  					"otherField": "otherValue",
    72  					"settings": map[string]interface{}{
    73  						"diskAutoresize": true,
    74  					},
    75  				},
    76  			},
    77  			resourceExists: true,
    78  			inputConfig: map[string]interface{}{
    79  				"otherField": "otherValue",
    80  				"settings": map[string]interface{}{
    81  					"diskAutoresize": true,
    82  					"diskSize":       50,
    83  				},
    84  			},
    85  			expectedConfig: map[string]interface{}{
    86  				"otherField": "otherValue",
    87  				"settings": map[string]interface{}{
    88  					"diskAutoresize": true,
    89  				},
    90  			},
    91  		},
    92  		{
    93  			name: "ComputeBackendService treats backends as unmanaged when backends not set",
    94  			kind: "ComputeBackendService",
    95  			lastAppliedConfig: map[string]interface{}{
    96  				"spec": map[string]interface{}{
    97  					"otherField": "otherValue",
    98  				},
    99  			},
   100  			resourceExists: true,
   101  			inputConfig: map[string]interface{}{
   102  				"otherField": "otherValue",
   103  				"backend":    []interface{}{},
   104  			},
   105  			expectedConfig: map[string]interface{}{
   106  				"otherField": "otherValue",
   107  			},
   108  		},
   109  		{
   110  			name: "ComputeBackendService manages user-supplied backends",
   111  			kind: "ComputeBackendService",
   112  			lastAppliedConfig: map[string]interface{}{
   113  				"spec": map[string]interface{}{
   114  					"otherField": "otherValue",
   115  					"backend": []interface{}{
   116  						"backend1",
   117  					},
   118  				},
   119  			},
   120  			resourceExists: true,
   121  			inputConfig: map[string]interface{}{
   122  				"otherField": "otherValue",
   123  				"backend": []interface{}{
   124  					"backend1",
   125  				},
   126  			},
   127  			expectedConfig: map[string]interface{}{
   128  				"otherField": "otherValue",
   129  				"backend": []interface{}{
   130  					"backend1",
   131  				},
   132  			},
   133  		},
   134  		{
   135  			name: "user-applied value is explicit override of GCP",
   136  			kind: "ContainerCluster",
   137  			lastAppliedConfig: map[string]interface{}{
   138  				"spec": map[string]interface{}{
   139  					"otherField": "otherValue",
   140  					"releaseChannel": map[string]interface{}{
   141  						"channel": "REGULAR",
   142  					},
   143  					"nodeVersion": "1.14",
   144  				},
   145  			},
   146  			resourceExists: true,
   147  			inputConfig: map[string]interface{}{
   148  				"otherField": "otherValue",
   149  				"releaseChannel": map[string]interface{}{
   150  					"channel": "REGULAR",
   151  				},
   152  				"nodeVersion": "1.14",
   153  			},
   154  			expectedConfig: map[string]interface{}{
   155  				"otherField": "otherValue",
   156  				"releaseChannel": map[string]interface{}{
   157  					"channel": "REGULAR",
   158  				},
   159  				"nodeVersion": "1.14",
   160  			},
   161  		},
   162  		{
   163  			name:           "use explicit config when creating resource",
   164  			kind:           "ContainerCluster",
   165  			resourceExists: false,
   166  			inputConfig: map[string]interface{}{
   167  				"otherField": "otherValue",
   168  				"releaseChannel": map[string]interface{}{
   169  					"channel": "REGULAR",
   170  				},
   171  				"nodeVersion": "1.14",
   172  			},
   173  			expectedConfig: map[string]interface{}{
   174  				"otherField": "otherValue",
   175  				"releaseChannel": map[string]interface{}{
   176  					"channel": "REGULAR",
   177  				},
   178  				"nodeVersion": "1.14",
   179  			},
   180  		},
   181  		{
   182  			name: "ContainerNodePool treats version field as GCP-managed when autoUpgrade set",
   183  			kind: "ContainerNodePool",
   184  			lastAppliedConfig: map[string]interface{}{
   185  				"spec": map[string]interface{}{
   186  					"otherField": "otherValue",
   187  					"management": map[string]interface{}{
   188  						"autoUpgrade": true,
   189  					},
   190  				},
   191  			},
   192  			resourceExists: true,
   193  			inputConfig: map[string]interface{}{
   194  				"otherField": "otherValue",
   195  				"management": map[string]interface{}{
   196  					"autoUpgrade": true,
   197  				},
   198  				"version": "1.18.0-gke.0",
   199  			},
   200  			expectedConfig: map[string]interface{}{
   201  				"otherField": "otherValue",
   202  				"management": map[string]interface{}{
   203  					"autoUpgrade": true,
   204  				},
   205  			},
   206  		},
   207  		{
   208  			name: "ContainerNodePool uses spec value when autoUpgrade turned off",
   209  			kind: "ContainerNodePool",
   210  			lastAppliedConfig: map[string]interface{}{
   211  				"spec": map[string]interface{}{
   212  					"otherField": "otherValue",
   213  					"management": map[string]interface{}{
   214  						"autoUpgrade": false,
   215  					},
   216  				},
   217  			},
   218  			resourceExists: true,
   219  			inputConfig: map[string]interface{}{
   220  				"otherField": "otherValue",
   221  				"management": map[string]interface{}{
   222  					"autoUpgrade": false,
   223  				},
   224  				"version": "1.18.0-gke.0",
   225  			},
   226  			expectedConfig: map[string]interface{}{
   227  				"otherField": "otherValue",
   228  				"management": map[string]interface{}{
   229  					"autoUpgrade": false,
   230  				},
   231  				"version": "1.18.0-gke.0",
   232  			},
   233  		},
   234  
   235  		{
   236  			name: "ContainerNodePool uses user-supplied version when explicitly applied",
   237  			kind: "ContainerNodePool",
   238  			lastAppliedConfig: map[string]interface{}{
   239  				"spec": map[string]interface{}{
   240  					"otherField": "otherValue",
   241  					"management": map[string]interface{}{
   242  						"autoUpgrade": true,
   243  					},
   244  					"version": "1.18.0-gke.0",
   245  				},
   246  			},
   247  			resourceExists: true,
   248  			inputConfig: map[string]interface{}{
   249  				"otherField": "otherValue",
   250  				"management": map[string]interface{}{
   251  					"autoUpgrade": true,
   252  				},
   253  				"version": "1.18.0-gke.0",
   254  			},
   255  			expectedConfig: map[string]interface{}{
   256  				"otherField": "otherValue",
   257  				"management": map[string]interface{}{
   258  					"autoUpgrade": true,
   259  				},
   260  				"version": "1.18.0-gke.0",
   261  			},
   262  		},
   263  		{
   264  			name: "ContainerNodePool treats initialNodeCount as GCP-managed",
   265  			kind: "ContainerNodePool",
   266  			lastAppliedConfig: map[string]interface{}{
   267  				"spec": map[string]interface{}{
   268  					"otherField": "otherValue",
   269  				},
   270  			},
   271  			resourceExists: true,
   272  			inputConfig: map[string]interface{}{
   273  				"otherField":       "otherValue",
   274  				"initialNodeCount": 1,
   275  			},
   276  			expectedConfig: map[string]interface{}{
   277  				"otherField": "otherValue",
   278  			},
   279  		},
   280  		{
   281  			name: "ContainerNodePool treats nodeCount as GCP-managed if autoscaling set",
   282  			kind: "ContainerNodePool",
   283  			lastAppliedConfig: map[string]interface{}{
   284  				"spec": map[string]interface{}{
   285  					"otherField": "otherValue",
   286  					"autoscaling": map[string]interface{}{
   287  						"minNodeCount": 1,
   288  						"maxNodeCount": 3,
   289  					},
   290  				},
   291  			},
   292  			resourceExists: true,
   293  			inputConfig: map[string]interface{}{
   294  				"otherField": "otherValue",
   295  				"autoscaling": map[string]interface{}{
   296  					"minNodeCount": 1,
   297  					"maxNodeCount": 3,
   298  				},
   299  				"nodeCount": 1,
   300  			},
   301  			expectedConfig: map[string]interface{}{
   302  				"otherField": "otherValue",
   303  				"autoscaling": map[string]interface{}{
   304  					"minNodeCount": 1,
   305  					"maxNodeCount": 3,
   306  				},
   307  			},
   308  		},
   309  		{
   310  			name: "ContainerNodePool uses explicit nodeCount when autoscaling disabled",
   311  			kind: "ContainerNodePool",
   312  			lastAppliedConfig: map[string]interface{}{
   313  				"spec": map[string]interface{}{
   314  					"otherField": "otherValue",
   315  				},
   316  			},
   317  			resourceExists: true,
   318  			inputConfig: map[string]interface{}{
   319  				"otherField": "otherValue",
   320  				"nodeCount":  1,
   321  			},
   322  			expectedConfig: map[string]interface{}{
   323  				"otherField": "otherValue",
   324  				"nodeCount":  1,
   325  			},
   326  		},
   327  		{
   328  			name: "ContainerNodePool uses explicit nodeCount when autoscaling set explicitly to nil",
   329  			kind: "ContainerNodePool",
   330  			lastAppliedConfig: map[string]interface{}{
   331  				"spec": map[string]interface{}{
   332  					"otherField":  "otherValue",
   333  					"autoscaling": nil,
   334  				},
   335  			},
   336  			resourceExists: true,
   337  			inputConfig: map[string]interface{}{
   338  				"otherField":  "otherValue",
   339  				"autoscaling": nil,
   340  				"nodeCount":   1,
   341  			},
   342  			expectedConfig: map[string]interface{}{
   343  				"otherField":  "otherValue",
   344  				"autoscaling": nil,
   345  				"nodeCount":   1,
   346  			},
   347  		},
   348  		{
   349  			name: "BigtableInstance removes numNodes when not set",
   350  			kind: "BigtableInstance",
   351  			lastAppliedConfig: map[string]interface{}{
   352  				"spec": map[string]interface{}{
   353  					"otherField": "otherValue",
   354  					"cluster": []interface{}{
   355  						map[string]interface{}{
   356  							"clusterId": "test1",
   357  							"zone":      "us-central1-a",
   358  						},
   359  						map[string]interface{}{
   360  							"clusterId": "test2",
   361  							"zone":      "us-west1-a",
   362  						},
   363  					},
   364  				},
   365  			},
   366  			resourceExists: true,
   367  			inputConfig: map[string]interface{}{
   368  				"otherField": "otherValue",
   369  				"cluster": []interface{}{
   370  					map[string]interface{}{
   371  						"clusterId": "test1",
   372  						"zone":      "us-central1-a",
   373  						"numNodes":  float64(1),
   374  					},
   375  					map[string]interface{}{
   376  						"clusterId": "test2",
   377  						"zone":      "us-west1-a",
   378  						"numNodes":  float64(1),
   379  					},
   380  				},
   381  			},
   382  			expectedConfig: map[string]interface{}{
   383  				"otherField": "otherValue",
   384  				"cluster": []interface{}{
   385  					map[string]interface{}{
   386  						"clusterId": "test1",
   387  						"zone":      "us-central1-a",
   388  					},
   389  					map[string]interface{}{
   390  						"clusterId": "test2",
   391  						"zone":      "us-west1-a",
   392  					},
   393  				},
   394  			},
   395  		},
   396  		{
   397  			name: "BigtableInstance respects numNodes when specified",
   398  			kind: "BigtableInstance",
   399  			lastAppliedConfig: map[string]interface{}{
   400  				"spec": map[string]interface{}{
   401  					"otherField": "otherValue",
   402  					"cluster": []interface{}{
   403  						map[string]interface{}{
   404  							"clusterId": "test1",
   405  							"zone":      "us-central1-a",
   406  							"numNodes":  float64(2),
   407  						},
   408  						map[string]interface{}{
   409  							"clusterId": "test2",
   410  							"zone":      "us-west1-a",
   411  							"numNodes":  float64(3),
   412  						},
   413  					},
   414  				},
   415  			},
   416  			resourceExists: true,
   417  			inputConfig: map[string]interface{}{
   418  				"otherField": "otherValue",
   419  				"cluster": []interface{}{
   420  					map[string]interface{}{
   421  						"clusterId": "test1",
   422  						"zone":      "us-central1-a",
   423  						"numNodes":  float64(2),
   424  					},
   425  					map[string]interface{}{
   426  						"clusterId": "test2",
   427  						"zone":      "us-west1-a",
   428  						"numNodes":  float64(3),
   429  					},
   430  				},
   431  			},
   432  			expectedConfig: map[string]interface{}{
   433  				"otherField": "otherValue",
   434  				"cluster": []interface{}{
   435  					map[string]interface{}{
   436  						"clusterId": "test1",
   437  						"zone":      "us-central1-a",
   438  						"numNodes":  float64(2),
   439  					},
   440  					map[string]interface{}{
   441  						"clusterId": "test2",
   442  						"zone":      "us-west1-a",
   443  						"numNodes":  float64(3),
   444  					},
   445  				},
   446  			},
   447  		},
   448  		{
   449  			name: "BigtableInstance handles mixed set and unset numNodes",
   450  			kind: "BigtableInstance",
   451  			lastAppliedConfig: map[string]interface{}{
   452  				"spec": map[string]interface{}{
   453  					"otherField": "otherValue",
   454  					"cluster": []interface{}{
   455  						map[string]interface{}{
   456  							"clusterId": "test1",
   457  							"zone":      "us-central1-a",
   458  							"numNodes":  float64(2),
   459  						},
   460  						map[string]interface{}{
   461  							"clusterId": "test2",
   462  							"zone":      "us-west1-a",
   463  						},
   464  					},
   465  				},
   466  			},
   467  			resourceExists: true,
   468  			inputConfig: map[string]interface{}{
   469  				"otherField": "otherValue",
   470  				"cluster": []interface{}{
   471  					map[string]interface{}{
   472  						"clusterId": "test1",
   473  						"zone":      "us-central1-a",
   474  						"numNodes":  float64(2),
   475  					},
   476  					map[string]interface{}{
   477  						"clusterId": "test2",
   478  						"zone":      "us-west1-a",
   479  						"numNodes":  float64(3),
   480  					},
   481  				},
   482  			},
   483  			expectedConfig: map[string]interface{}{
   484  				"otherField": "otherValue",
   485  				"cluster": []interface{}{
   486  					map[string]interface{}{
   487  						"clusterId": "test1",
   488  						"zone":      "us-central1-a",
   489  						"numNodes":  float64(2),
   490  					},
   491  					map[string]interface{}{
   492  						"clusterId": "test2",
   493  						"zone":      "us-west1-a",
   494  					},
   495  				},
   496  			},
   497  		},
   498  		{
   499  			name: "BigtableInstance removes an existing numNodes, if the value is removed",
   500  			kind: "BigtableInstance",
   501  			lastAppliedConfig: map[string]interface{}{
   502  				"spec": map[string]interface{}{
   503  					"otherField": "otherValue",
   504  					"cluster": []interface{}{
   505  						map[string]interface{}{
   506  							"clusterId": "test1",
   507  							"zone":      "us-central1-a",
   508  							"numNodes":  float64(2),
   509  						},
   510  					},
   511  				},
   512  			},
   513  			resourceExists: true,
   514  			inputConfig: map[string]interface{}{
   515  				"otherField": "otherValue",
   516  				"cluster": []interface{}{
   517  					map[string]interface{}{
   518  						"clusterId": "test1",
   519  						"zone":      "us-central1-a",
   520  					},
   521  				},
   522  			},
   523  			expectedConfig: map[string]interface{}{
   524  				"otherField": "otherValue",
   525  				"cluster": []interface{}{
   526  					map[string]interface{}{
   527  						"clusterId": "test1",
   528  						"zone":      "us-central1-a",
   529  					},
   530  				},
   531  			},
   532  		},
   533  	}
   534  	for _, tc := range tests {
   535  		t.Run(tc.name, func(t *testing.T) {
   536  			r := resourceSkeleton()
   537  			r.SetGroupVersionKind(schema.GroupVersionKind{Kind: tc.kind})
   538  			lastAppliedConfigJSON, err := json.Marshal(tc.lastAppliedConfig)
   539  			if err != nil {
   540  				t.Fatalf("error marshaling last applied config: %v", err)
   541  			}
   542  			r.SetAnnotations(map[string]string{
   543  				k8s.LastAppliedConfigurationAnnotation: string(lastAppliedConfigJSON),
   544  			})
   545  			var liveState *terraform.InstanceState
   546  			if tc.resourceExists {
   547  				// The content of the instance state does not matter here,
   548  				// as the diff function later handles the merge. We just
   549  				// need to supply *something* to mark this resource
   550  				// as already existing.
   551  				liveState = &terraform.InstanceState{
   552  					ID: "foo",
   553  				}
   554  			}
   555  			config := tc.inputConfig
   556  			if err := ResolveLegacyGCPManagedFields(r, liveState, config); err != nil {
   557  				t.Fatalf("error resolving GCP-managed fields: %v", err)
   558  			}
   559  			if !test.Equals(t, tc.expectedConfig, config) {
   560  				t.Fatalf("expected config: %+v, actual: %+v", tc.expectedConfig, config)
   561  			}
   562  		})
   563  	}
   564  }
   565  

View as plain text