...

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

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

     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 k8s_test
    16  
    17  import (
    18  	"encoding/json"
    19  	"testing"
    20  
    21  	corekccv1alpha1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/core/v1alpha1"
    22  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/deepcopy"
    23  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
    24  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test"
    25  	testk8s "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/test/k8s"
    26  
    27  	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    28  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    30  )
    31  
    32  var emptyObject = make(map[string]interface{})
    33  
    34  func TestConstructManagedFieldSet(t *testing.T) {
    35  	tests := []struct {
    36  		name                string
    37  		managedFieldEntries []v1.ManagedFieldsEntry
    38  		expectedSet         *fieldpath.Set
    39  	}{
    40  		{
    41  			name: "fields from separate managers are combined",
    42  			managedFieldEntries: []v1.ManagedFieldsEntry{
    43  				mapToManagedFieldEntry(t, "managerA", map[string]interface{}{
    44  					"f:spec": map[string]interface{}{
    45  						".":              emptyObject,
    46  						"f:simpleFieldA": emptyObject,
    47  						"f:nestedObjectA": map[string]interface{}{
    48  							".":              emptyObject,
    49  							"f:nestedFieldA": emptyObject,
    50  						},
    51  					},
    52  				}),
    53  				mapToManagedFieldEntry(t, "managerB", map[string]interface{}{
    54  					"f:spec": map[string]interface{}{
    55  						".":              emptyObject,
    56  						"f:simpleFieldB": emptyObject,
    57  						"f:nestedObjectB": map[string]interface{}{
    58  							".":              emptyObject,
    59  							"f:nestedFieldB": emptyObject,
    60  						},
    61  					},
    62  				}),
    63  			},
    64  			expectedSet: testk8s.MapToFieldPathSet(t, map[string]interface{}{
    65  				"f:simpleFieldA": emptyObject,
    66  				"f:simpleFieldB": emptyObject,
    67  				"f:nestedObjectA": map[string]interface{}{
    68  					".":              emptyObject,
    69  					"f:nestedFieldA": emptyObject,
    70  				},
    71  				"f:nestedObjectB": map[string]interface{}{
    72  					".":              emptyObject,
    73  					"f:nestedFieldB": emptyObject,
    74  				},
    75  			}),
    76  		},
    77  		{
    78  			name: "fields from the cnrm-controller-manager manager are ignored",
    79  			managedFieldEntries: []v1.ManagedFieldsEntry{
    80  				mapToManagedFieldEntry(t, "managerA", map[string]interface{}{
    81  					"f:spec": map[string]interface{}{
    82  						".":              emptyObject,
    83  						"f:simpleFieldA": emptyObject,
    84  						"f:nestedObjectA": map[string]interface{}{
    85  							".":              emptyObject,
    86  							"f:nestedFieldA": emptyObject,
    87  						},
    88  					},
    89  				}),
    90  				mapToManagedFieldEntry(t, k8s.ControllerManagedFieldManager, map[string]interface{}{
    91  					"f:spec": map[string]interface{}{
    92  						".":                emptyObject,
    93  						"f:simpleFieldKCC": emptyObject,
    94  						"f:nestedObjectKCC": map[string]interface{}{
    95  							".":                emptyObject,
    96  							"f:nestedFieldKCC": emptyObject,
    97  						},
    98  					},
    99  				}),
   100  			},
   101  			expectedSet: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   102  				"f:simpleFieldA": emptyObject,
   103  				"f:nestedObjectA": map[string]interface{}{
   104  					".":              emptyObject,
   105  					"f:nestedFieldA": emptyObject,
   106  				},
   107  			}),
   108  		},
   109  	}
   110  	for _, tc := range tests {
   111  		t.Run(tc.name, func(t *testing.T) {
   112  			managedFieldSet, err := k8s.ConstructManagedFieldsV1Set(tc.managedFieldEntries)
   113  			if err != nil {
   114  				t.Error("error constructing managed field set:", err)
   115  				return
   116  			}
   117  			if !managedFieldSet.Equals(tc.expectedSet) {
   118  				t.Errorf("actual and expected sets do not match: actual: %v, expected: %v",
   119  					string(fieldPathSetToJSON(t, managedFieldSet)),
   120  					string(fieldPathSetToJSON(t, tc.expectedSet)))
   121  				return
   122  			}
   123  		})
   124  	}
   125  }
   126  
   127  var schema = &apiextensions.JSONSchemaProps{
   128  	Properties: map[string]apiextensions.JSONSchemaProps{
   129  		"spec": {
   130  			Properties: map[string]apiextensions.JSONSchemaProps{
   131  				"field":     {Type: "string"},
   132  				"external":  {Type: "string"},
   133  				"unrelated": {Type: "string"},
   134  				"projectRef": {
   135  					Properties: map[string]apiextensions.JSONSchemaProps{
   136  						"external":  {Type: "string"},
   137  						"name":      {Type: "string"},
   138  						"namespace": {Type: "string"},
   139  					},
   140  					Type: "object",
   141  				},
   142  				"obj": {
   143  					Properties: map[string]apiextensions.JSONSchemaProps{
   144  						"field":    {Type: "string"},
   145  						"external": {Type: "string"},
   146  						"nestedObj": {
   147  							Properties: map[string]apiextensions.JSONSchemaProps{
   148  								"field":    {Type: "string"},
   149  								"external": {Type: "string"},
   150  							},
   151  							Type: "object",
   152  						},
   153  						"nestedList": {
   154  							Items: &apiextensions.JSONSchemaPropsOrArray{
   155  								Schema: &apiextensions.JSONSchemaProps{Type: "string"},
   156  							},
   157  							Type: "array",
   158  						},
   159  					},
   160  					Type: "object",
   161  				},
   162  				"objMap": {
   163  					AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   164  						Schema: &apiextensions.JSONSchemaProps{Type: "string"},
   165  					},
   166  					Type: "object",
   167  				},
   168  				"objMapSchemaless": {
   169  					Type: "object",
   170  				},
   171  				"list": {
   172  					Items: &apiextensions.JSONSchemaPropsOrArray{
   173  						Schema: &apiextensions.JSONSchemaProps{Type: "string"},
   174  					},
   175  					Type: "array",
   176  				},
   177  			},
   178  			Type: "object",
   179  		},
   180  	},
   181  }
   182  
   183  func TestOverlayManagedFieldsOntoState(t *testing.T) {
   184  	tests := []struct {
   185  		name             string
   186  		spec             map[string]interface{}
   187  		krmState         map[string]interface{}
   188  		managedFields    *fieldpath.Set
   189  		hierarchicalRefs []corekccv1alpha1.HierarchicalReference
   190  		expected         map[string]interface{}
   191  	}{
   192  		{
   193  			name: "use spec values for k8s-managed fields",
   194  			spec: map[string]interface{}{
   195  				"field": "k8s",
   196  			},
   197  			krmState: map[string]interface{}{
   198  				"field": "external",
   199  			},
   200  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   201  				"f:field": emptyObject,
   202  			}),
   203  			expected: map[string]interface{}{
   204  				"field": "k8s",
   205  			},
   206  		},
   207  		{
   208  			name: "use state values for externally-managed fields",
   209  			spec: map[string]interface{}{
   210  				"field": "k8s",
   211  			},
   212  			krmState: map[string]interface{}{
   213  				"field": "external",
   214  			},
   215  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   216  				"f:unrelated": emptyObject,
   217  			}),
   218  			expected: map[string]interface{}{
   219  				"field": "external",
   220  			},
   221  		},
   222  		{
   223  			name: "use spec values for k8s-managed nested fields",
   224  			spec: map[string]interface{}{
   225  				"obj": map[string]interface{}{
   226  					"field": "k8s",
   227  				},
   228  			},
   229  			krmState: map[string]interface{}{
   230  				"obj": map[string]interface{}{
   231  					"field": "external",
   232  				},
   233  			},
   234  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   235  				"f:obj": map[string]interface{}{
   236  					".":       emptyObject,
   237  					"f:field": emptyObject,
   238  				},
   239  			}),
   240  			expected: map[string]interface{}{
   241  				"obj": map[string]interface{}{
   242  					"field": "k8s",
   243  				},
   244  			},
   245  		},
   246  		{
   247  			name: "use state value for externally-managed nested fields",
   248  			spec: map[string]interface{}{
   249  				"obj": map[string]interface{}{
   250  					"field": "k8s",
   251  				},
   252  			},
   253  			krmState: map[string]interface{}{
   254  				"obj": map[string]interface{}{
   255  					"field": "external",
   256  				},
   257  			},
   258  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   259  				"f:unrelated": emptyObject,
   260  			}),
   261  			expected: map[string]interface{}{
   262  				"obj": map[string]interface{}{
   263  					"field": "external",
   264  				},
   265  			},
   266  		},
   267  		{
   268  			name: "top-level mixed management fields are merged",
   269  			spec: map[string]interface{}{
   270  				"field":    "k8s",
   271  				"external": "k8s",
   272  			},
   273  			krmState: map[string]interface{}{
   274  				"field":    "external",
   275  				"external": "external",
   276  			},
   277  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   278  				"f:field": emptyObject,
   279  			}),
   280  			expected: map[string]interface{}{
   281  				"field":    "k8s",
   282  				"external": "external",
   283  			},
   284  		},
   285  		{
   286  			name: "nested mixed management fields are merged",
   287  			spec: map[string]interface{}{
   288  				"obj": map[string]interface{}{
   289  					"field":    "k8s",
   290  					"external": "k8s",
   291  				},
   292  			},
   293  			krmState: map[string]interface{}{
   294  				"obj": map[string]interface{}{
   295  					"field":    "external",
   296  					"external": "external",
   297  				},
   298  			},
   299  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   300  				"f:obj": map[string]interface{}{
   301  					".":       emptyObject,
   302  					"f:field": emptyObject,
   303  				},
   304  			}),
   305  			expected: map[string]interface{}{
   306  				"obj": map[string]interface{}{
   307  					"field":    "k8s",
   308  					"external": "external",
   309  				},
   310  			},
   311  		},
   312  		{
   313  			name: "object map fields are merged",
   314  			spec: map[string]interface{}{
   315  				"objMap": map[string]interface{}{
   316  					"field":    "k8s",
   317  					"external": "k8s",
   318  				},
   319  			},
   320  			krmState: map[string]interface{}{
   321  				"objMap": map[string]interface{}{
   322  					"field":    "external",
   323  					"external": "external",
   324  				},
   325  			},
   326  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   327  				"f:objMap": map[string]interface{}{
   328  					"f:field": emptyObject,
   329  				},
   330  			}),
   331  			expected: map[string]interface{}{
   332  				"objMap": map[string]interface{}{
   333  					"field":    "k8s",
   334  					"external": "external",
   335  				},
   336  			},
   337  		},
   338  		{
   339  			name: "schemaless object map fields are merged",
   340  			spec: map[string]interface{}{
   341  				"objMapSchemaless": map[string]interface{}{
   342  					"field":    "k8s",
   343  					"external": "k8s",
   344  				},
   345  			},
   346  			krmState: map[string]interface{}{
   347  				"objMapSchemaless": map[string]interface{}{
   348  					"field":    "external",
   349  					"external": "external",
   350  				},
   351  			},
   352  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   353  				"f:objMapSchemaless": map[string]interface{}{
   354  					"f:field": emptyObject,
   355  				},
   356  			}),
   357  			expected: map[string]interface{}{
   358  				"objMapSchemaless": map[string]interface{}{
   359  					"field":    "k8s",
   360  					"external": "external",
   361  				},
   362  			},
   363  		},
   364  
   365  		{
   366  			// TODO(b/160160236): Externally-managed list merging is not yet
   367  			//  supported.
   368  			name: "always use k8s value for lists set in spec",
   369  			spec: map[string]interface{}{
   370  				"list": []interface{}{"k8s-first", "k8s-second"},
   371  			},
   372  			krmState: map[string]interface{}{
   373  				"list": []interface{}{"external-first", "external-second"},
   374  			},
   375  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   376  				"f:unrelated": emptyObject,
   377  			}),
   378  			expected: map[string]interface{}{
   379  				"list": []interface{}{"k8s-first", "k8s-second"},
   380  			},
   381  		},
   382  		{
   383  			name: "use external value for lists not set in spec",
   384  			spec: map[string]interface{}{
   385  				"field": "k8s",
   386  			},
   387  			krmState: map[string]interface{}{
   388  				"field": "external",
   389  				"list":  []interface{}{"external-first", "external-second"},
   390  			},
   391  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   392  				"f:field": emptyObject,
   393  			}),
   394  			expected: map[string]interface{}{
   395  				"field": "k8s",
   396  				"list":  []interface{}{"external-first", "external-second"},
   397  			},
   398  		},
   399  		{
   400  			// name-only acquisition case
   401  			name: "empty spec and managed fields are supported",
   402  			spec: nil,
   403  			krmState: map[string]interface{}{
   404  				"field": "external",
   405  			},
   406  			managedFields: nil,
   407  			expected: map[string]interface{}{
   408  				"field": "external",
   409  			},
   410  		},
   411  		{
   412  			name: "empty state is supported",
   413  			spec: map[string]interface{}{
   414  				"field": "k8s",
   415  			},
   416  			krmState: nil,
   417  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   418  				"f:field": emptyObject,
   419  			}),
   420  			expected: map[string]interface{}{
   421  				"field": "k8s",
   422  			},
   423  		},
   424  		{
   425  			name: "externally-managed fields can be cleared",
   426  			spec: map[string]interface{}{
   427  				"field":     "external",
   428  				"unrelated": "val",
   429  			},
   430  			krmState: map[string]interface{}{
   431  				"unrelated": "val",
   432  			},
   433  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   434  				"f:unrelated": emptyObject,
   435  			}),
   436  			expected: map[string]interface{}{
   437  				"unrelated": "val",
   438  			},
   439  		},
   440  		{
   441  			name: "hierarchical reference is preserved",
   442  			spec: map[string]interface{}{
   443  				"projectRef": map[string]interface{}{
   444  					"external": "project_id",
   445  				},
   446  				"unrelated": "val",
   447  			},
   448  			krmState: map[string]interface{}{
   449  				"unrelated": "val",
   450  			},
   451  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   452  				"f:unrelated": emptyObject,
   453  				"f:projectRef": map[string]interface{}{
   454  					".":          emptyObject,
   455  					"f:external": emptyObject,
   456  				},
   457  			}),
   458  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   459  				{
   460  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   461  					Key:  "projectRef",
   462  				},
   463  			},
   464  			expected: map[string]interface{}{
   465  				"projectRef": map[string]interface{}{
   466  					"external": "project_id",
   467  				},
   468  				"unrelated": "val",
   469  			},
   470  		},
   471  		{
   472  			name: "hierarchical reference is preserved even if not in managed fields as long as in spec",
   473  			spec: map[string]interface{}{
   474  				"projectRef": map[string]interface{}{
   475  					"external": "project_id",
   476  				},
   477  				"unrelated": "val",
   478  			},
   479  			krmState: map[string]interface{}{
   480  				"unrelated": "val",
   481  			},
   482  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   483  				"f:unrelated": emptyObject,
   484  			}),
   485  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   486  				{
   487  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   488  					Key:  "projectRef",
   489  				},
   490  			},
   491  			expected: map[string]interface{}{
   492  				"projectRef": map[string]interface{}{
   493  					"external": "project_id",
   494  				},
   495  				"unrelated": "val",
   496  			},
   497  		},
   498  		{
   499  			name: "no hierarchical reference in output config if none in spec",
   500  			spec: map[string]interface{}{
   501  				"unrelated": "val",
   502  			},
   503  			krmState: map[string]interface{}{
   504  				"unrelated": "val",
   505  			},
   506  			managedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   507  				"f:unrelated": emptyObject,
   508  			}),
   509  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   510  				{
   511  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   512  					Key:  "projectRef",
   513  				},
   514  			},
   515  			expected: map[string]interface{}{
   516  				"unrelated": "val",
   517  			},
   518  		},
   519  	}
   520  	for _, tc := range tests {
   521  		t.Run(tc.name, func(t *testing.T) {
   522  			spec := deepcopy.MapStringInterface(tc.spec)
   523  			res, err := k8s.OverlayManagedFieldsOntoState(spec, tc.krmState, tc.managedFields, schema, tc.hierarchicalRefs)
   524  			if err != nil {
   525  				t.Error("error overlaying externally-managed fields:", err)
   526  				return
   527  			}
   528  			if !test.Equals(t, res, tc.expected) {
   529  				t.Errorf("actual: %+v, expected: %+v", res, tc.expected)
   530  				return
   531  			}
   532  		})
   533  	}
   534  }
   535  
   536  func TestConstructTrimmedSpecWithManagedFields(t *testing.T) {
   537  	tests := []struct {
   538  		name             string
   539  		resource         *k8s.Resource
   540  		hierarchicalRefs []corekccv1alpha1.HierarchicalReference
   541  		expected         map[string]interface{}
   542  	}{
   543  		{
   544  			name: "no managed field information present",
   545  			resource: &k8s.Resource{
   546  				Spec: map[string]interface{}{
   547  					"field": "value",
   548  					"list":  []interface{}{"a", "b"},
   549  				},
   550  			},
   551  			expected: map[string]interface{}{
   552  				"field": "value",
   553  				"list":  []interface{}{"a", "b"},
   554  			},
   555  		},
   556  		{
   557  			name: "preserve k8s managed fields on the top level",
   558  			resource: &k8s.Resource{
   559  				Spec: map[string]interface{}{
   560  					"field":    "k8s",
   561  					"external": "external",
   562  				},
   563  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   564  					"f:field": emptyObject,
   565  				}),
   566  			},
   567  			expected: map[string]interface{}{
   568  				"field": "k8s",
   569  			},
   570  		},
   571  		{
   572  			name: "preserve k8s-managed nested fields",
   573  			resource: &k8s.Resource{
   574  				Spec: map[string]interface{}{
   575  					"obj": map[string]interface{}{
   576  						"field": "k8s",
   577  						"nestedObj": map[string]interface{}{
   578  							"field":    "k8s",
   579  							"external": "external",
   580  						},
   581  						"external": "external",
   582  					},
   583  				},
   584  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   585  					"f:obj": map[string]interface{}{
   586  						".":       emptyObject,
   587  						"f:field": emptyObject,
   588  						"f:nestedObj": map[string]interface{}{
   589  							".":       emptyObject,
   590  							"f:field": emptyObject,
   591  						},
   592  					},
   593  				}),
   594  			},
   595  			expected: map[string]interface{}{
   596  				"obj": map[string]interface{}{
   597  					"field": "k8s",
   598  					"nestedObj": map[string]interface{}{
   599  						"field": "k8s",
   600  					},
   601  				},
   602  			},
   603  		},
   604  		{
   605  			// TODO(b/160160236): Externally-managed list merging is not yet
   606  			//  supported.
   607  			name: "always use k8s value for lists set in spec",
   608  			resource: &k8s.Resource{
   609  				Spec: map[string]interface{}{
   610  					"list": []interface{}{"k8s-first", "k8s-second"},
   611  				},
   612  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   613  					"f:unrelated": emptyObject,
   614  				}),
   615  			},
   616  			expected: map[string]interface{}{
   617  				"list": []interface{}{"k8s-first", "k8s-second"},
   618  			},
   619  		},
   620  		{
   621  			name:     "empty spec and managed fields are supported",
   622  			resource: &k8s.Resource{},
   623  			expected: nil,
   624  		},
   625  		{
   626  			name: "hierarchical reference is preserved",
   627  			resource: &k8s.Resource{
   628  				Spec: map[string]interface{}{
   629  					"projectRef": map[string]interface{}{
   630  						"external": "project_id",
   631  					},
   632  					"unrelated": "val",
   633  				},
   634  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   635  					"f:unrelated": emptyObject,
   636  					"f:projectRef": map[string]interface{}{
   637  						".":          emptyObject,
   638  						"f:external": emptyObject,
   639  					},
   640  				}),
   641  			},
   642  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   643  				{
   644  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   645  					Key:  "projectRef",
   646  				},
   647  			},
   648  			expected: map[string]interface{}{
   649  				"projectRef": map[string]interface{}{
   650  					"external": "project_id",
   651  				},
   652  				"unrelated": "val",
   653  			},
   654  		},
   655  		{
   656  			name: "hierarchical reference is preserved even if not in managed fields as long as in spec",
   657  			resource: &k8s.Resource{
   658  				Spec: map[string]interface{}{
   659  					"projectRef": map[string]interface{}{
   660  						"external": "project_id",
   661  					},
   662  					"unrelated": "val",
   663  				},
   664  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   665  					"f:unrelated": emptyObject,
   666  				}),
   667  			},
   668  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   669  				{
   670  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   671  					Key:  "projectRef",
   672  				},
   673  			},
   674  			expected: map[string]interface{}{
   675  				"projectRef": map[string]interface{}{
   676  					"external": "project_id",
   677  				},
   678  				"unrelated": "val",
   679  			},
   680  		},
   681  		{
   682  			name: "no hierarchical reference in output config if none in spec",
   683  			resource: &k8s.Resource{
   684  				Spec: map[string]interface{}{
   685  					"unrelated": "val",
   686  				},
   687  				ManagedFields: testk8s.MapToFieldPathSet(t, map[string]interface{}{
   688  					"f:unrelated": emptyObject,
   689  				}),
   690  			},
   691  			hierarchicalRefs: []corekccv1alpha1.HierarchicalReference{
   692  				{
   693  					Type: corekccv1alpha1.HierarchicalReferenceTypeProject,
   694  					Key:  "projectRef",
   695  				},
   696  			},
   697  			expected: map[string]interface{}{
   698  				"unrelated": "val",
   699  			},
   700  		},
   701  	}
   702  	for _, tc := range tests {
   703  		tc := tc
   704  		t.Run(tc.name, func(t *testing.T) {
   705  			t.Parallel()
   706  			trimmedSpec, err := k8s.ConstructTrimmedSpecWithManagedFields(tc.resource, schema, tc.hierarchicalRefs)
   707  			if err != nil {
   708  				t.Fatalf("unexpected error: %v", err)
   709  			}
   710  			if got, want := trimmedSpec, tc.expected; !test.Equals(t, got, want) {
   711  				t.Fatalf("got: %v, want: %v", got, want)
   712  			}
   713  		})
   714  	}
   715  }
   716  
   717  func fieldPathSetToJSON(t *testing.T, s *fieldpath.Set) []byte {
   718  	b, err := s.ToJSON()
   719  	if err != nil {
   720  		t.Fatal("error converting set to JSON:", err)
   721  	}
   722  	return b
   723  }
   724  
   725  func mapToManagedFieldEntry(t *testing.T, manager string, fields map[string]interface{}) v1.ManagedFieldsEntry {
   726  	b, err := json.Marshal(fields)
   727  	if err != nil {
   728  		t.Fatal("error marshaling to JSON:", err)
   729  	}
   730  	return v1.ManagedFieldsEntry{
   731  		Manager:    manager,
   732  		FieldsType: k8s.ManagedFieldsTypeFieldsV1,
   733  		FieldsV1:   &v1.FieldsV1{Raw: b},
   734  	}
   735  }
   736  

View as plain text