...

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

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

     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 livestate_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/dcl/livestate"
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/nasa9084/go-openapi"
    25  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    26  )
    27  
    28  var testDCLSchema = &openapi.Schema{
    29  	Type: "object",
    30  	Properties: map[string]*openapi.Schema{
    31  		"project": &openapi.Schema{
    32  			Type: "string",
    33  		},
    34  		"name": &openapi.Schema{
    35  			Type: "string",
    36  		},
    37  		"labels": &openapi.Schema{
    38  			Type: "object",
    39  			AdditionalProperties: &openapi.Schema{
    40  				Type: "string",
    41  			},
    42  		},
    43  		"stringKey": {
    44  			Type: "string",
    45  		},
    46  		"intKey": {
    47  			Type: "integer",
    48  		},
    49  		"boolKey": {
    50  			Type: "boolean",
    51  		},
    52  		"floatKey": {
    53  			Type: "number",
    54  		},
    55  		"numberKey": {
    56  			Type: "number",
    57  		},
    58  		"nestedObjectKey": {
    59  			Type: "object",
    60  			Properties: map[string]*openapi.Schema{
    61  				"nestedField1": {
    62  					Type: "boolean",
    63  				},
    64  				"nestedField2": {
    65  					Type: "string",
    66  				},
    67  				"nestedReferenceKey": {
    68  					Type: "string",
    69  					Extension: map[string]interface{}{
    70  						"x-dcl-references": []interface{}{
    71  							map[interface{}]interface{}{
    72  								"resource": "Test1/Bar",
    73  								"path":     "statusField",
    74  							},
    75  						},
    76  					},
    77  				},
    78  				"nestedSensitiveField": {
    79  					Type: "string",
    80  					Extension: map[string]interface{}{
    81  						"x-dcl-sensitive": true,
    82  					},
    83  				},
    84  				"nestedStatusField": {
    85  					Type:     "string",
    86  					ReadOnly: true,
    87  				},
    88  				"nestedStatusArrayString": {
    89  					Type:     "array",
    90  					ReadOnly: true,
    91  					Items: &openapi.Schema{
    92  						Type:     "string",
    93  						ReadOnly: true,
    94  					},
    95  				},
    96  			},
    97  			Required: []string{"nestedField1"},
    98  		},
    99  		"sensitiveField": {
   100  			Type: "string",
   101  			Extension: map[string]interface{}{
   102  				"x-dcl-sensitive": true,
   103  			},
   104  		},
   105  		"referenceKey": {
   106  			Type: "string",
   107  			Extension: map[string]interface{}{
   108  				"x-dcl-references": []interface{}{
   109  					map[interface{}]interface{}{
   110  						"resource": "test1/Bar",
   111  						"path":     "name",
   112  					},
   113  				},
   114  			},
   115  		},
   116  		"mapKey": {
   117  			Type: "object",
   118  			AdditionalProperties: &openapi.Schema{
   119  				Type: "string",
   120  			},
   121  		},
   122  		"stringObjectMapKey": {
   123  			Type: "object",
   124  			AdditionalProperties: &openapi.Schema{
   125  				Type: "object",
   126  				Properties: map[string]*openapi.Schema{
   127  					"objectField1": {
   128  						Type: "number",
   129  					},
   130  					"objectField2": {
   131  						Type: "string",
   132  					},
   133  					"state": {
   134  						ReadOnly: true,
   135  						Type:     "string",
   136  					},
   137  					"objectReferenceArrayKey": {
   138  						Type: "array",
   139  						Items: &openapi.Schema{
   140  							Type: "string",
   141  							Extension: map[string]interface{}{
   142  								"x-dcl-references": []interface{}{
   143  									map[interface{}]interface{}{
   144  										"resource": "test1/Bar",
   145  										"path":     "name",
   146  									},
   147  								},
   148  							},
   149  						},
   150  					},
   151  				},
   152  			},
   153  		},
   154  		"primitiveArrayKey": {
   155  			Type: "array",
   156  			Items: &openapi.Schema{
   157  				Type: "string",
   158  			},
   159  		},
   160  		"objectArrayKey": {
   161  			Type: "array",
   162  			Items: &openapi.Schema{
   163  				Type: "object",
   164  				Properties: map[string]*openapi.Schema{
   165  					"field1": {
   166  						Type: "number",
   167  					},
   168  					"field2": {
   169  						Type: "string",
   170  					},
   171  					"sensitiveFieldInArray": {
   172  						Type: "string",
   173  						Extension: map[string]interface{}{
   174  							"x-dcl-sensitive": true,
   175  						},
   176  					},
   177  					"bar": {
   178  						Type: "string",
   179  						Extension: map[string]interface{}{
   180  							"x-dcl-references": []interface{}{
   181  								map[interface{}]interface{}{
   182  									"resource": "test1/Bar",
   183  									"path":     "name",
   184  								},
   185  							},
   186  						},
   187  					},
   188  				},
   189  			},
   190  		},
   191  		"referenceArrayKey": {
   192  			Type: "array",
   193  			Items: &openapi.Schema{
   194  				Type: "string",
   195  				Extension: map[string]interface{}{
   196  					"x-dcl-references": []interface{}{
   197  						map[interface{}]interface{}{
   198  							"resource": "test1/Bar",
   199  							"path":     "name",
   200  						},
   201  					},
   202  				},
   203  			},
   204  		},
   205  		"statusField": {
   206  			Type:     "string",
   207  			ReadOnly: true,
   208  		},
   209  	},
   210  	Extension: map[string]interface{}{
   211  		"x-dcl-parent-container": "project",
   212  	},
   213  }
   214  
   215  func TestSetMutableButUnreadableFields(t *testing.T) {
   216  	tests := []struct {
   217  		name                     string
   218  		krmObj                   *unstructured.Unstructured
   219  		mutableButUnreadableSpec map[string]interface{}
   220  		path                     []string
   221  		schema                   *openapi.Schema
   222  		expectedResult           *unstructured.Unstructured
   223  		hasError                 bool
   224  	}{
   225  		{
   226  			name: "set primitive fields",
   227  			krmObj: &unstructured.Unstructured{
   228  				Object: map[string]interface{}{},
   229  			},
   230  			mutableButUnreadableSpec: map[string]interface{}{
   231  				"boolKey":   true,
   232  				"floatKey":  1.1,
   233  				"intKey":    10,
   234  				"stringKey": "testValue",
   235  			},
   236  			path:   []string{"spec"},
   237  			schema: testDCLSchema,
   238  			expectedResult: &unstructured.Unstructured{
   239  				Object: map[string]interface{}{
   240  					"spec": map[string]interface{}{
   241  						"boolKey":   true,
   242  						"floatKey":  1.1,
   243  						"intKey":    10,
   244  						"stringKey": "testValue",
   245  					},
   246  				},
   247  			},
   248  		},
   249  		{
   250  			name: "set primitive array",
   251  			krmObj: &unstructured.Unstructured{
   252  				Object: map[string]interface{}{},
   253  			},
   254  			mutableButUnreadableSpec: map[string]interface{}{
   255  				"primitiveArrayKey": []interface{}{"test1", "test2"},
   256  			},
   257  			path:   []string{"spec"},
   258  			schema: testDCLSchema,
   259  			expectedResult: &unstructured.Unstructured{
   260  				Object: map[string]interface{}{
   261  					"spec": map[string]interface{}{
   262  						"primitiveArrayKey": []interface{}{"test1", "test2"},
   263  					},
   264  				},
   265  			},
   266  		},
   267  		{
   268  			name: "set nested fields",
   269  			krmObj: &unstructured.Unstructured{
   270  				Object: map[string]interface{}{},
   271  			},
   272  			mutableButUnreadableSpec: map[string]interface{}{
   273  				"nestedObjectKey": map[string]interface{}{
   274  					"nestedField1": true,
   275  					"nestedField2": "test",
   276  				},
   277  			},
   278  			path:   []string{"spec"},
   279  			schema: testDCLSchema,
   280  			expectedResult: &unstructured.Unstructured{
   281  				Object: map[string]interface{}{
   282  					"spec": map[string]interface{}{
   283  						"nestedObjectKey": map[string]interface{}{
   284  							"nestedField1": true,
   285  							"nestedField2": "test",
   286  						},
   287  					},
   288  				},
   289  			},
   290  		},
   291  		{
   292  			name: "set map",
   293  			krmObj: &unstructured.Unstructured{
   294  				Object: map[string]interface{}{},
   295  			},
   296  			mutableButUnreadableSpec: map[string]interface{}{
   297  				"mapKey": map[string]interface{}{
   298  					"label1": "value1",
   299  					"label2": "value2",
   300  				},
   301  			},
   302  			path:   []string{"spec"},
   303  			schema: testDCLSchema,
   304  			expectedResult: &unstructured.Unstructured{
   305  				Object: map[string]interface{}{
   306  					"spec": map[string]interface{}{
   307  						"mapKey": map[string]interface{}{
   308  							"label1": "value1",
   309  							"label2": "value2",
   310  						},
   311  					},
   312  				},
   313  			},
   314  		},
   315  		{
   316  			name: "non primitive array should return an error",
   317  			krmObj: &unstructured.Unstructured{
   318  				Object: map[string]interface{}{},
   319  			},
   320  			mutableButUnreadableSpec: map[string]interface{}{
   321  				"objectArrayKey": map[string]interface{}{
   322  					"field1": 10,
   323  				},
   324  			},
   325  			path:     []string{"spec"},
   326  			schema:   testDCLSchema,
   327  			hasError: true,
   328  		},
   329  		{
   330  			name: "overwrite the existent value in krmObj with the value in" +
   331  				"mutableButUnreadableSpec",
   332  			krmObj: &unstructured.Unstructured{
   333  				Object: map[string]interface{}{
   334  					"spec": map[string]interface{}{
   335  						"nestedObjectKey": map[string]interface{}{
   336  							"nestedField1": true,
   337  							"nestedField2": "oldVal",
   338  						},
   339  					},
   340  				},
   341  			},
   342  			mutableButUnreadableSpec: map[string]interface{}{
   343  				"nestedObjectKey": map[string]interface{}{
   344  					"nestedField2": "newVal",
   345  				},
   346  			},
   347  			path:   []string{"spec"},
   348  			schema: testDCLSchema,
   349  			expectedResult: &unstructured.Unstructured{
   350  				Object: map[string]interface{}{
   351  					"spec": map[string]interface{}{
   352  						"nestedObjectKey": map[string]interface{}{
   353  							"nestedField1": true,
   354  							"nestedField2": "newVal",
   355  						},
   356  					},
   357  				},
   358  			},
   359  		},
   360  		{
   361  			name: "set sensitive path with a plain text value",
   362  			krmObj: &unstructured.Unstructured{
   363  				Object: map[string]interface{}{},
   364  			},
   365  			mutableButUnreadableSpec: map[string]interface{}{
   366  				"sensitiveField": map[string]interface{}{
   367  					"value": "sensitive-value",
   368  				},
   369  			},
   370  			path:   []string{"spec"},
   371  			schema: testDCLSchema,
   372  			expectedResult: &unstructured.Unstructured{
   373  				Object: map[string]interface{}{
   374  					"spec": map[string]interface{}{
   375  						"sensitiveField": map[string]interface{}{
   376  							"value": "sensitive-value",
   377  						},
   378  					},
   379  				},
   380  			},
   381  		},
   382  	}
   383  
   384  	for _, tc := range tests {
   385  		tc := tc
   386  		t.Run(tc.name, func(t *testing.T) {
   387  			t.Parallel()
   388  			result, err := livestate.SetMutableButUnreadableFields(tc.krmObj, tc.mutableButUnreadableSpec, tc.path, tc.schema, nil, "", nil)
   389  			if tc.hasError {
   390  				if err == nil {
   391  					t.Fatalf("got nil, but want an error setting mutable-but-unreadable fields")
   392  				}
   393  				return
   394  			}
   395  			if err != nil {
   396  				t.Fatalf("error setting mutable-but-unreadable fields: %v", err)
   397  			}
   398  
   399  			if got, want := result, tc.expectedResult; !test.Equals(t, got, want) {
   400  				t.Fatalf("unexpected spec diff (-want +got): \n%v", cmp.Diff(want, got))
   401  			}
   402  		})
   403  	}
   404  }
   405  

View as plain text