...

Source file src/k8s.io/apimachinery/pkg/util/managedfields/fieldmanager_test.go

Documentation: k8s.io/apimachinery/pkg/util/managedfields

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package managedfields_test
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"path/filepath"
    25  	"reflect"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    31  	"k8s.io/apimachinery/pkg/api/meta"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/apimachinery/pkg/runtime/schema"
    36  	"k8s.io/apimachinery/pkg/util/managedfields"
    37  	"k8s.io/apimachinery/pkg/util/managedfields/internal"
    38  	"k8s.io/apimachinery/pkg/util/managedfields/managedfieldstest"
    39  	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
    40  	"k8s.io/kube-openapi/pkg/validation/spec"
    41  	"sigs.k8s.io/yaml"
    42  )
    43  
    44  var fakeTypeConverter = func() managedfields.TypeConverter {
    45  	data, err := os.ReadFile(filepath.Join(strings.Repeat(".."+string(filepath.Separator), 7),
    46  		"api", "openapi-spec", "swagger.json"))
    47  	if err != nil {
    48  		panic(err)
    49  	}
    50  	swag := spec.Swagger{}
    51  	if err := json.Unmarshal(data, &swag); err != nil {
    52  		panic(err)
    53  	}
    54  	convertedDefs := map[string]*spec.Schema{}
    55  	for k, v := range swag.Definitions {
    56  		vCopy := v
    57  		convertedDefs[k] = &vCopy
    58  	}
    59  	typeConverter, err := managedfields.NewTypeConverter(convertedDefs, false)
    60  	if err != nil {
    61  		panic(err)
    62  	}
    63  	return typeConverter
    64  }()
    65  
    66  // TestUpdateApplyConflict tests that applying to an object, which
    67  // wasn't created by apply, will give conflicts
    68  func TestUpdateApplyConflict(t *testing.T) {
    69  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
    70  
    71  	patch := []byte(`{
    72  		"apiVersion": "apps/v1",
    73  		"kind": "Deployment",
    74  		"metadata": {
    75  			"name": "deployment",
    76  			"labels": {"app": "nginx"}
    77  		},
    78  		"spec": {
    79                          "replicas": 3,
    80                          "selector": {
    81                                  "matchLabels": {
    82                                           "app": "nginx"
    83                                  }
    84                          },
    85                          "template": {
    86                                  "metadata": {
    87                                          "labels": {
    88                                                  "app": "nginx"
    89                                          }
    90                                  },
    91                                  "spec": {
    92  				        "containers": [{
    93  					        "name":  "nginx",
    94  					        "image": "nginx:latest"
    95  				        }]
    96                                  }
    97                          }
    98  		}
    99  	}`)
   100  	newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   101  	if err := yaml.Unmarshal(patch, &newObj.Object); err != nil {
   102  		t.Fatalf("error decoding YAML: %v", err)
   103  	}
   104  
   105  	if err := f.Update(newObj, "fieldmanager_test"); err != nil {
   106  		t.Fatalf("failed to apply object: %v", err)
   107  	}
   108  
   109  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   110  	if err := yaml.Unmarshal([]byte(`{
   111  		"apiVersion": "apps/v1",
   112  		"kind": "Deployment",
   113  		"metadata": {
   114  			"name": "deployment",
   115  		},
   116  		"spec": {
   117  			"replicas": 101,
   118  		}
   119  	}`), &appliedObj.Object); err != nil {
   120  		t.Fatalf("error decoding YAML: %v", err)
   121  	}
   122  
   123  	err := f.Apply(appliedObj, "fieldmanager_conflict", false)
   124  	if err == nil || !apierrors.IsConflict(err) {
   125  		t.Fatalf("Expecting to get conflicts but got %v", err)
   126  	}
   127  }
   128  
   129  func TestApplyStripsFields(t *testing.T) {
   130  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   131  
   132  	newObj := &unstructured.Unstructured{
   133  		Object: map[string]interface{}{
   134  			"apiVersion": "apps/v1",
   135  			"kind":       "Deployment",
   136  		},
   137  	}
   138  
   139  	newObj.SetName("b")
   140  	newObj.SetNamespace("b")
   141  	newObj.SetUID("b")
   142  	newObj.SetGeneration(0)
   143  	newObj.SetResourceVersion("b")
   144  	newObj.SetCreationTimestamp(metav1.NewTime(time.Now()))
   145  	newObj.SetManagedFields([]metav1.ManagedFieldsEntry{
   146  		{
   147  			Manager:    "update",
   148  			Operation:  metav1.ManagedFieldsOperationApply,
   149  			APIVersion: "apps/v1",
   150  		},
   151  	})
   152  	if err := f.Update(newObj, "fieldmanager_test"); err != nil {
   153  		t.Fatalf("failed to apply object: %v", err)
   154  	}
   155  
   156  	if m := f.ManagedFields(); len(m) != 0 {
   157  		t.Fatalf("fields did not get stripped: %v", m)
   158  	}
   159  }
   160  
   161  func TestVersionCheck(t *testing.T) {
   162  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   163  
   164  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   165  	if err := yaml.Unmarshal([]byte(`{
   166  		"apiVersion": "apps/v1",
   167  		"kind": "Deployment",
   168  	}`), &appliedObj.Object); err != nil {
   169  		t.Fatalf("error decoding YAML: %v", err)
   170  	}
   171  
   172  	// patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors
   173  	err := f.Apply(appliedObj, "fieldmanager_test", false)
   174  	if err != nil {
   175  		t.Fatalf("failed to apply object: %v", err)
   176  	}
   177  
   178  	appliedObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   179  	if err := yaml.Unmarshal([]byte(`{
   180  		"apiVersion": "apps/v1beta1",
   181  		"kind": "Deployment",
   182  	}`), &appliedObj.Object); err != nil {
   183  		t.Fatalf("error decoding YAML: %v", err)
   184  	}
   185  
   186  	// patch has 'apiVersion: apps/v1beta1' but live version is apps/v1 -> error
   187  	err = f.Apply(appliedObj, "fieldmanager_test", false)
   188  	if err == nil {
   189  		t.Fatalf("expected an error from mismatched patch and live versions")
   190  	}
   191  	switch typ := err.(type) {
   192  	default:
   193  		t.Fatalf("expected error to be of type %T was %T (%v)", apierrors.StatusError{}, typ, err)
   194  	case apierrors.APIStatus:
   195  		if typ.Status().Code != http.StatusBadRequest {
   196  			t.Fatalf("expected status code to be %d but was %d",
   197  				http.StatusBadRequest, typ.Status().Code)
   198  		}
   199  	}
   200  }
   201  
   202  func TestVersionCheckDoesNotPanic(t *testing.T) {
   203  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   204  
   205  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   206  	if err := yaml.Unmarshal([]byte(`{
   207  		"apiVersion": "apps/v1",
   208  		"kind": "Deployment",
   209  	}`), &appliedObj.Object); err != nil {
   210  		t.Fatalf("error decoding YAML: %v", err)
   211  	}
   212  
   213  	// patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors
   214  	err := f.Apply(appliedObj, "fieldmanager_test", false)
   215  	if err != nil {
   216  		t.Fatalf("failed to apply object: %v", err)
   217  	}
   218  
   219  	appliedObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   220  	if err := yaml.Unmarshal([]byte(`{
   221  		}`), &appliedObj.Object); err != nil {
   222  		t.Fatalf("error decoding YAML: %v", err)
   223  	}
   224  
   225  	// patch has 'apiVersion: apps/v2' but live version is apps/v1 -> error
   226  	err = f.Apply(appliedObj, "fieldmanager_test", false)
   227  	if err == nil {
   228  		t.Fatalf("expected an error from mismatched patch and live versions")
   229  	}
   230  	switch typ := err.(type) {
   231  	default:
   232  		t.Fatalf("expected error to be of type %T was %T (%v)", apierrors.StatusError{}, typ, err)
   233  	case apierrors.APIStatus:
   234  		if typ.Status().Code != http.StatusBadRequest {
   235  			t.Fatalf("expected status code to be %d but was %d",
   236  				http.StatusBadRequest, typ.Status().Code)
   237  		}
   238  	}
   239  }
   240  
   241  func TestApplyDoesNotStripLabels(t *testing.T) {
   242  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   243  
   244  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   245  	if err := yaml.Unmarshal([]byte(`{
   246  		"apiVersion": "v1",
   247  		"kind": "Pod",
   248  		"metadata": {
   249  			"labels": {
   250  				"a": "b"
   251  			},
   252  		}
   253  	}`), &appliedObj.Object); err != nil {
   254  		t.Fatalf("error decoding YAML: %v", err)
   255  	}
   256  
   257  	err := f.Apply(appliedObj, "fieldmanager_test", false)
   258  	if err != nil {
   259  		t.Fatalf("failed to apply object: %v", err)
   260  	}
   261  
   262  	if m := f.ManagedFields(); len(m) != 1 {
   263  		t.Fatalf("labels shouldn't get stripped on apply: %v", m)
   264  	}
   265  }
   266  
   267  func getObjectBytes(file string) []byte {
   268  	s, err := os.ReadFile(file)
   269  	if err != nil {
   270  		panic(err)
   271  	}
   272  	return s
   273  }
   274  
   275  func TestApplyNewObject(t *testing.T) {
   276  	tests := []struct {
   277  		gvk schema.GroupVersionKind
   278  		obj []byte
   279  	}{
   280  		{
   281  			gvk: schema.FromAPIVersionAndKind("v1", "Pod"),
   282  			obj: getObjectBytes("pod.yaml"),
   283  		},
   284  		{
   285  			gvk: schema.FromAPIVersionAndKind("v1", "Node"),
   286  			obj: getObjectBytes("node.yaml"),
   287  		},
   288  		{
   289  			gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"),
   290  			obj: getObjectBytes("endpoints.yaml"),
   291  		},
   292  	}
   293  
   294  	for _, test := range tests {
   295  		t.Run(test.gvk.String(), func(t *testing.T) {
   296  			f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, test.gvk)
   297  
   298  			appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   299  			if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil {
   300  				t.Fatalf("error decoding YAML: %v", err)
   301  			}
   302  
   303  			if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil {
   304  				t.Fatal(err)
   305  			}
   306  		})
   307  	}
   308  }
   309  
   310  func TestApplyFailsWithManagedFields(t *testing.T) {
   311  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   312  
   313  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   314  	if err := yaml.Unmarshal([]byte(`{
   315  		"apiVersion": "v1",
   316  		"kind": "Pod",
   317  		"metadata": {
   318  			"managedFields": [
   319  				{
   320  				  "manager": "test",
   321  				}
   322  			]
   323  		}
   324  	}`), &appliedObj.Object); err != nil {
   325  		t.Fatalf("error decoding YAML: %v", err)
   326  	}
   327  
   328  	err := f.Apply(appliedObj, "fieldmanager_test", false)
   329  
   330  	if err == nil {
   331  		t.Fatalf("successfully applied with set managed fields")
   332  	}
   333  }
   334  
   335  func TestApplySuccessWithNoManagedFields(t *testing.T) {
   336  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   337  
   338  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   339  	if err := yaml.Unmarshal([]byte(`{
   340  		"apiVersion": "v1",
   341  		"kind": "Pod",
   342  		"metadata": {
   343  			"labels": {
   344  				"a": "b"
   345  			},
   346  		}
   347  	}`), &appliedObj.Object); err != nil {
   348  		t.Fatalf("error decoding YAML: %v", err)
   349  	}
   350  	err := f.Apply(appliedObj, "fieldmanager_test", false)
   351  
   352  	if err != nil {
   353  		t.Fatalf("failed to apply object: %v", err)
   354  	}
   355  }
   356  
   357  // Run an update and apply, and make sure that nothing has changed.
   358  func TestNoOpChanges(t *testing.T) {
   359  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   360  
   361  	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   362  	if err := yaml.Unmarshal([]byte(`{
   363  		"apiVersion": "v1",
   364  		"kind": "Pod",
   365  		"metadata": {
   366  			"labels": {
   367  				"a": "b"
   368  			},
   369  			"creationTimestamp": null,
   370  		}
   371  	}`), &obj.Object); err != nil {
   372  		t.Fatalf("error decoding YAML: %v", err)
   373  	}
   374  	if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", false); err != nil {
   375  		t.Fatalf("failed to apply object: %v", err)
   376  	}
   377  	before := f.Live()
   378  	// Wait to make sure the timestamp is different
   379  	time.Sleep(time.Second)
   380  	// Applying with a different fieldmanager will create an entry..
   381  	if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply_other", false); err != nil {
   382  		t.Fatalf("failed to update object: %v", err)
   383  	}
   384  	if reflect.DeepEqual(before, f.Live()) {
   385  		t.Fatalf("Applying no-op apply with new manager didn't change object: \n%v", f.Live())
   386  	}
   387  	before = f.Live()
   388  	// Wait to make sure the timestamp is different
   389  	time.Sleep(time.Second)
   390  	if err := f.Update(obj.DeepCopyObject(), "fieldmanager_test_update"); err != nil {
   391  		t.Fatalf("failed to update object: %v", err)
   392  	}
   393  	if !reflect.DeepEqual(before, f.Live()) {
   394  		t.Fatalf("No-op update has changed the object:\n%v\n---\n%v", before, f.Live())
   395  	}
   396  	before = f.Live()
   397  	// Wait to make sure the timestamp is different
   398  	time.Sleep(time.Second)
   399  	if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", true); err != nil {
   400  		t.Fatalf("failed to re-apply object: %v", err)
   401  	}
   402  	if !reflect.DeepEqual(before, f.Live()) {
   403  		t.Fatalf("No-op apply has changed the object:\n%v\n---\n%v", before, f.Live())
   404  	}
   405  }
   406  
   407  // Tests that one can reset the managedFields by sending either an empty
   408  // list
   409  func TestResetManagedFieldsEmptyList(t *testing.T) {
   410  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   411  
   412  	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   413  	if err := yaml.Unmarshal([]byte(`{
   414  		"apiVersion": "v1",
   415  		"kind": "Pod",
   416  		"metadata": {
   417  			"labels": {
   418  				"a": "b"
   419  			},
   420  		}
   421  	}`), &obj.Object); err != nil {
   422  		t.Fatalf("error decoding YAML: %v", err)
   423  	}
   424  	if err := f.Apply(obj, "fieldmanager_test_apply", false); err != nil {
   425  		t.Fatalf("failed to apply object: %v", err)
   426  	}
   427  
   428  	if err := yaml.Unmarshal([]byte(`{
   429  		"apiVersion": "v1",
   430  		"kind": "Pod",
   431  		"metadata": {
   432  			"managedFields": [],
   433  			"labels": {
   434  				"a": "b"
   435  			},
   436  		}
   437  	}`), &obj.Object); err != nil {
   438  		t.Fatalf("error decoding YAML: %v", err)
   439  	}
   440  	if err := f.Update(obj, "update_manager"); err != nil {
   441  		t.Fatalf("failed to update with empty manager: %v", err)
   442  	}
   443  
   444  	if len(f.ManagedFields()) != 0 {
   445  		t.Fatalf("failed to reset managedFields: %v", f.ManagedFields())
   446  	}
   447  }
   448  
   449  // Tests that one can reset the managedFields by sending either a list with one empty item.
   450  func TestResetManagedFieldsEmptyItem(t *testing.T) {
   451  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"))
   452  
   453  	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   454  	if err := yaml.Unmarshal([]byte(`{
   455  		"apiVersion": "v1",
   456  		"kind": "Pod",
   457  		"metadata": {
   458  			"labels": {
   459  				"a": "b"
   460  			},
   461  		}
   462  	}`), &obj.Object); err != nil {
   463  		t.Fatalf("error decoding YAML: %v", err)
   464  	}
   465  	if err := f.Apply(obj, "fieldmanager_test_apply", false); err != nil {
   466  		t.Fatalf("failed to apply object: %v", err)
   467  	}
   468  
   469  	if err := yaml.Unmarshal([]byte(`{
   470  		"apiVersion": "v1",
   471  		"kind": "Pod",
   472  		"metadata": {
   473  			"managedFields": [{}],
   474  			"labels": {
   475  				"a": "b"
   476  			},
   477  		}
   478  	}`), &obj.Object); err != nil {
   479  		t.Fatalf("error decoding YAML: %v", err)
   480  	}
   481  	if err := f.Update(obj, "update_manager"); err != nil {
   482  		t.Fatalf("failed to update with empty manager: %v", err)
   483  	}
   484  
   485  	if len(f.ManagedFields()) != 0 {
   486  		t.Fatalf("failed to reset managedFields: %v", f.ManagedFields())
   487  	}
   488  }
   489  
   490  func TestServerSideApplyWithInvalidLastApplied(t *testing.T) {
   491  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   492  
   493  	// create object with client-side apply
   494  	newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   495  	deployment := []byte(`
   496  apiVersion: apps/v1
   497  kind: Deployment
   498  metadata:
   499    name: my-deployment
   500    labels:
   501      app: my-app-v1
   502  spec:
   503    replicas: 1
   504  `)
   505  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   506  		t.Errorf("error decoding YAML: %v", err)
   507  	}
   508  
   509  	invalidLastApplied := "invalid-object"
   510  	if err := internal.SetLastApplied(newObj, invalidLastApplied); err != nil {
   511  		t.Errorf("failed to set last applied: %v", err)
   512  	}
   513  
   514  	if err := f.Update(newObj, "kubectl-client-side-apply-test"); err != nil {
   515  		t.Errorf("failed to update object: %v", err)
   516  	}
   517  
   518  	lastApplied, err := getLastApplied(f.Live())
   519  	if err != nil {
   520  		t.Errorf("failed to get last applied: %v", err)
   521  	}
   522  	if lastApplied != invalidLastApplied {
   523  		t.Errorf("expected last applied annotation to be set to %q, but got: %q", invalidLastApplied, lastApplied)
   524  	}
   525  
   526  	// upgrade management of the object from client-side apply to server-side apply
   527  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   528  	appliedDeployment := []byte(`
   529  apiVersion: apps/v1
   530  kind: Deployment
   531  metadata:
   532    name: my-deployment
   533    labels:
   534      app: my-app-v2
   535  spec:
   536    replicas: 100
   537  `)
   538  	if err := yaml.Unmarshal(appliedDeployment, &appliedObj.Object); err != nil {
   539  		t.Errorf("error decoding YAML: %v", err)
   540  	}
   541  
   542  	if err := f.Apply(appliedObj, "kubectl", false); err == nil || !apierrors.IsConflict(err) {
   543  		t.Errorf("expected conflict when applying with invalid last-applied annotation, but got no error for object: \n%+v", appliedObj)
   544  	}
   545  
   546  	lastApplied, err = getLastApplied(f.Live())
   547  	if err != nil {
   548  		t.Errorf("failed to get last applied: %v", err)
   549  	}
   550  	if lastApplied != invalidLastApplied {
   551  		t.Errorf("expected last applied annotation to be NOT be updated, but got: %q", lastApplied)
   552  	}
   553  
   554  	// force server-side apply should work and fix the annotation
   555  	if err := f.Apply(appliedObj, "kubectl", true); err != nil {
   556  		t.Errorf("failed to force server-side apply with: %v", err)
   557  	}
   558  
   559  	lastApplied, err = getLastApplied(f.Live())
   560  	if err != nil {
   561  		t.Errorf("failed to get last applied: %v", err)
   562  	}
   563  	if lastApplied == invalidLastApplied ||
   564  		!strings.Contains(lastApplied, "my-app-v2") {
   565  		t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied)
   566  	}
   567  }
   568  
   569  func TestInteropForClientSideApplyAndServerSideApply(t *testing.T) {
   570  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   571  
   572  	// create object with client-side apply
   573  	newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   574  	deployment := []byte(`
   575  apiVersion: apps/v1
   576  kind: Deployment
   577  metadata:
   578    name: my-deployment
   579    labels:
   580      app: my-app
   581  spec:
   582    replicas: 100
   583    selector:
   584      matchLabels:
   585        app: my-app
   586    template:
   587      metadata:
   588        labels:
   589          app: my-app
   590      spec:
   591        containers:
   592        - name: my-c
   593          image: my-image-v1
   594  `)
   595  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   596  		t.Errorf("error decoding YAML: %v", err)
   597  	}
   598  	if err := setLastAppliedFromEncoded(newObj, deployment); err != nil {
   599  		t.Errorf("failed to set last applied: %v", err)
   600  	}
   601  
   602  	if err := f.Update(newObj, "kubectl-client-side-apply-test"); err != nil {
   603  		t.Errorf("failed to update object: %v", err)
   604  	}
   605  	lastApplied, err := getLastApplied(f.Live())
   606  	if err != nil {
   607  		t.Errorf("failed to get last applied: %v", err)
   608  	}
   609  	if !strings.Contains(lastApplied, "my-image-v1") {
   610  		t.Errorf("expected last applied annotation to be set properly, but got: %q", lastApplied)
   611  	}
   612  
   613  	// upgrade management of the object from client-side apply to server-side apply
   614  	appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   615  	appliedDeployment := []byte(`
   616  apiVersion: apps/v1
   617  kind: Deployment
   618  metadata:
   619    name: my-deployment
   620    labels:
   621      app: my-app-v2 # change
   622  spec:
   623    replicas: 8 # change
   624    selector:
   625      matchLabels:
   626        app: my-app-v2 # change
   627    template:
   628      metadata:
   629        labels:
   630          app: my-app-v2 # change
   631      spec:
   632        containers:
   633        - name: my-c
   634          image: my-image-v2 # change
   635  `)
   636  	if err := yaml.Unmarshal(appliedDeployment, &appliedObj.Object); err != nil {
   637  		t.Errorf("error decoding YAML: %v", err)
   638  	}
   639  
   640  	if err := f.Apply(appliedObj, "kubectl", false); err != nil {
   641  		t.Errorf("error applying object: %v", err)
   642  	}
   643  
   644  	lastApplied, err = getLastApplied(f.Live())
   645  	if err != nil {
   646  		t.Errorf("failed to get last applied: %v", err)
   647  	}
   648  	if !strings.Contains(lastApplied, "my-image-v2") {
   649  		t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied)
   650  	}
   651  }
   652  
   653  func TestNoTrackManagedFieldsForClientSideApply(t *testing.T) {
   654  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   655  
   656  	// create object
   657  	newObj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   658  	deployment := []byte(`
   659  apiVersion: apps/v1
   660  kind: Deployment
   661  metadata:
   662    name: my-deployment
   663    labels:
   664      app: my-app
   665  spec:
   666    replicas: 100
   667  `)
   668  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   669  		t.Errorf("error decoding YAML: %v", err)
   670  	}
   671  	if err := f.Update(newObj, "test_kubectl_create"); err != nil {
   672  		t.Errorf("failed to update object: %v", err)
   673  	}
   674  	if m := f.ManagedFields(); len(m) == 0 {
   675  		t.Errorf("expected to have managed fields, but got: %v", m)
   676  	}
   677  
   678  	// stop tracking managed fields
   679  	newObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   680  	deployment = []byte(`
   681  apiVersion: apps/v1
   682  kind: Deployment
   683  metadata:
   684    name: my-deployment
   685    managedFields: [] # stop tracking managed fields
   686    labels:
   687      app: my-app
   688  spec:
   689    replicas: 100
   690  `)
   691  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   692  		t.Errorf("error decoding YAML: %v", err)
   693  	}
   694  	newObj.SetUID("nonempty")
   695  	if err := f.Update(newObj, "test_kubectl_replace"); err != nil {
   696  		t.Errorf("failed to update object: %v", err)
   697  	}
   698  	if m := f.ManagedFields(); len(m) != 0 {
   699  		t.Errorf("expected to have stop tracking managed fields, but got: %v", m)
   700  	}
   701  
   702  	// check that we still don't track managed fields
   703  	newObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   704  	deployment = []byte(`
   705  apiVersion: apps/v1
   706  kind: Deployment
   707  metadata:
   708    name: my-deployment
   709    labels:
   710      app: my-app
   711  spec:
   712    replicas: 100
   713  `)
   714  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   715  		t.Errorf("error decoding YAML: %v", err)
   716  	}
   717  	if err := setLastAppliedFromEncoded(newObj, deployment); err != nil {
   718  		t.Errorf("failed to set last applied: %v", err)
   719  	}
   720  	if err := f.Update(newObj, "test_k_client_side_apply"); err != nil {
   721  		t.Errorf("failed to update object: %v", err)
   722  	}
   723  	if m := f.ManagedFields(); len(m) != 0 {
   724  		t.Errorf("expected to continue to not track managed fields, but got: %v", m)
   725  	}
   726  	lastApplied, err := getLastApplied(f.Live())
   727  	if err != nil {
   728  		t.Errorf("failed to get last applied: %v", err)
   729  	}
   730  	if !strings.Contains(lastApplied, "my-app") {
   731  		t.Errorf("expected last applied annotation to be set properly, but got: %q", lastApplied)
   732  	}
   733  
   734  	// start tracking managed fields
   735  	newObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   736  	deployment = []byte(`
   737  apiVersion: apps/v1
   738  kind: Deployment
   739  metadata:
   740    name: my-deployment
   741    labels:
   742      app: my-app
   743  spec:
   744    replicas: 100
   745  `)
   746  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   747  		t.Errorf("error decoding YAML: %v", err)
   748  	}
   749  	if err := f.Apply(newObj, "test_server_side_apply_without_upgrade", false); err != nil {
   750  		t.Errorf("error applying object: %v", err)
   751  	}
   752  	if m := f.ManagedFields(); len(m) < 2 {
   753  		t.Errorf("expected to start tracking managed fields with at least 2 field managers, but got: %v", m)
   754  	}
   755  	if e, a := "test_server_side_apply_without_upgrade", f.ManagedFields()[0].Manager; e != a {
   756  		t.Fatalf("exected first manager name to be %v, but got %v: %#v", e, a, f.ManagedFields())
   757  	}
   758  	if e, a := "before-first-apply", f.ManagedFields()[1].Manager; e != a {
   759  		t.Fatalf("exected second manager name to be %v, but got %v: %#v", e, a, f.ManagedFields())
   760  	}
   761  
   762  	// upgrade management of the object from client-side apply to server-side apply
   763  	newObj = &unstructured.Unstructured{Object: map[string]interface{}{}}
   764  	deployment = []byte(`
   765  apiVersion: apps/v1
   766  kind: Deployment
   767  metadata:
   768    name: my-deployment
   769    labels:
   770      app: my-app-v2 # change
   771  spec:
   772    replicas: 8 # change
   773  `)
   774  	if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil {
   775  		t.Errorf("error decoding YAML: %v", err)
   776  	}
   777  	if err := f.Apply(newObj, "kubectl", false); err != nil {
   778  		t.Errorf("error applying object: %v", err)
   779  	}
   780  	if m := f.ManagedFields(); len(m) == 0 {
   781  		t.Errorf("expected to track managed fields, but got: %v", m)
   782  	}
   783  	lastApplied, err = getLastApplied(f.Live())
   784  	if err != nil {
   785  		t.Errorf("failed to get last applied: %v", err)
   786  	}
   787  	if !strings.Contains(lastApplied, "my-app-v2") {
   788  		t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied)
   789  	}
   790  }
   791  
   792  func yamlToJSON(y []byte) (string, error) {
   793  	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   794  	if err := yaml.Unmarshal(y, &obj.Object); err != nil {
   795  		return "", fmt.Errorf("error decoding YAML: %v", err)
   796  	}
   797  	serialization, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
   798  	if err != nil {
   799  		return "", fmt.Errorf("error encoding object: %v", err)
   800  	}
   801  	json, err := yamlutil.ToJSON(serialization)
   802  	if err != nil {
   803  		return "", fmt.Errorf("error converting to json: %v", err)
   804  	}
   805  	return string(json), nil
   806  }
   807  
   808  func setLastAppliedFromEncoded(obj runtime.Object, lastApplied []byte) error {
   809  	lastAppliedJSON, err := yamlToJSON(lastApplied)
   810  	if err != nil {
   811  		return err
   812  	}
   813  	return internal.SetLastApplied(obj, lastAppliedJSON)
   814  }
   815  
   816  func getLastApplied(obj runtime.Object) (string, error) {
   817  	accessor := meta.NewAccessor()
   818  	annotations, err := accessor.Annotations(obj)
   819  	if err != nil {
   820  		return "", fmt.Errorf("failed to access annotations: %v", err)
   821  	}
   822  	if annotations == nil {
   823  		return "", fmt.Errorf("no annotations on obj: %v", obj)
   824  	}
   825  
   826  	lastApplied, ok := annotations[internal.LastAppliedConfigAnnotation]
   827  	if !ok {
   828  		return "", fmt.Errorf("expected last applied annotation, but got none for object: %v", obj)
   829  	}
   830  	return lastApplied, nil
   831  }
   832  
   833  func TestUpdateViaSubresources(t *testing.T) {
   834  	f := managedfieldstest.NewTestFieldManagerSubresource(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "scale")
   835  
   836  	obj := &unstructured.Unstructured{Object: map[string]interface{}{}}
   837  	if err := yaml.Unmarshal([]byte(`{
   838  		"apiVersion": "v1",
   839  		"kind": "Pod",
   840  		"metadata": {
   841  			"labels": {
   842  				"a":"b"
   843  			},
   844  		}
   845  	}`), &obj.Object); err != nil {
   846  		t.Fatalf("error decoding YAML: %v", err)
   847  	}
   848  	obj.SetManagedFields([]metav1.ManagedFieldsEntry{
   849  		{
   850  			Manager:    "test",
   851  			Operation:  metav1.ManagedFieldsOperationApply,
   852  			APIVersion: "apps/v1",
   853  			FieldsType: "FieldsV1",
   854  			FieldsV1: &metav1.FieldsV1{
   855  				Raw: []byte(`{"f:metadata":{"f:labels":{"f:another_field":{}}}}`),
   856  			},
   857  		},
   858  	})
   859  
   860  	// Check that managed fields cannot be changed explicitly via subresources
   861  	expectedManager := "fieldmanager_test_subresource"
   862  	if err := f.Update(obj, expectedManager); err != nil {
   863  		t.Fatalf("failed to apply object: %v", err)
   864  	}
   865  
   866  	managedFields := f.ManagedFields()
   867  	if len(managedFields) != 1 {
   868  		t.Fatalf("Expected new managed fields to have one entry. Got:\n%#v", managedFields)
   869  	}
   870  	if managedFields[0].Manager != expectedManager {
   871  		t.Fatalf("Expected first item to have manager set to: %s. Got: %s", expectedManager, managedFields[0].Manager)
   872  	}
   873  
   874  	// Check that managed fields cannot be reset via subresources
   875  	newObj := obj.DeepCopy()
   876  	newObj.SetManagedFields([]metav1.ManagedFieldsEntry{})
   877  	if err := f.Update(newObj, expectedManager); err != nil {
   878  		t.Fatalf("failed to apply object: %v", err)
   879  	}
   880  	newManagedFields := f.ManagedFields()
   881  	if len(newManagedFields) != 1 {
   882  		t.Fatalf("Expected new managed fields to have one entry. Got:\n%#v", newManagedFields)
   883  	}
   884  }
   885  
   886  // Ensures that a no-op Apply does not mutate managed fields
   887  func TestApplyDoesNotChangeManagedFields(t *testing.T) {
   888  	originalManagedFields := []metav1.ManagedFieldsEntry{}
   889  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter,
   890  		schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   891  	newObj := &unstructured.Unstructured{
   892  		Object: map[string]interface{}{},
   893  	}
   894  	appliedObj := &unstructured.Unstructured{
   895  		Object: map[string]interface{}{},
   896  	}
   897  
   898  	// Convert YAML string inputs to unstructured instances
   899  	if err := yaml.Unmarshal([]byte(`{
   900  		"apiVersion": "apps/v1",
   901  		"kind": "Deployment",
   902  		"metadata": {
   903  			"name": "deployment",
   904  			"labels": {"app": "nginx"}
   905  		},
   906  		"spec": {
   907  			"selector": {
   908  				"matchLabels": {
   909  					"app": "nginx"
   910  				}
   911  			},
   912  			"template": {
   913  				"metadata": {
   914  					"labels": {
   915  						"app": "nginx"
   916  					}
   917  				},
   918  				"spec": {
   919  					"containers": [{
   920  						"name":  "nginx",
   921  						"image": "nginx:latest"
   922  					}]
   923  				}
   924  			}
   925  		}
   926  	}`), &newObj.Object); err != nil {
   927  		t.Fatalf("error decoding YAML: %v", err)
   928  	}
   929  
   930  	if err := yaml.Unmarshal([]byte(`{
   931  		"apiVersion": "apps/v1",
   932  		"kind": "Deployment",
   933  		"metadata": {
   934  			"name": "deployment",
   935  		},
   936  		"spec": {
   937  			"replicas": 101,
   938  		}
   939  	}`), &appliedObj.Object); err != nil {
   940  		t.Fatalf("error decoding YAML: %v", err)
   941  	}
   942  
   943  	// Agent A applies initial configuration
   944  	if err := f.Apply(newObj.DeepCopyObject(), "fieldmanager_z", false); err != nil {
   945  		t.Fatalf("failed to apply object: %v", err)
   946  	}
   947  
   948  	// Agent B applies additive configuration
   949  	if err := f.Apply(appliedObj, "fieldmanager_b", false); err != nil {
   950  		t.Fatalf("failed to apply object %v", err)
   951  	}
   952  
   953  	// Next, agent A applies the initial configuration again, but we expect
   954  	// a no-op to managed fields.
   955  	//
   956  	// The following update is expected not to change the liveObj, save off
   957  	//	the fields
   958  	for _, field := range f.ManagedFields() {
   959  		originalManagedFields = append(originalManagedFields, *field.DeepCopy())
   960  	}
   961  
   962  	// Make sure timestamp change would be caught
   963  	time.Sleep(2 * time.Second)
   964  
   965  	if err := f.Apply(newObj, "fieldmanager_z", false); err != nil {
   966  		t.Fatalf("failed to apply object: %v", err)
   967  	}
   968  
   969  	// ensure that the live object is unchanged
   970  	if !reflect.DeepEqual(originalManagedFields, f.ManagedFields()) {
   971  		originalYAML, _ := yaml.Marshal(originalManagedFields)
   972  		current, _ := yaml.Marshal(f.ManagedFields())
   973  
   974  		// should have been a no-op w.r.t. managed fields
   975  		t.Fatalf("managed fields changed: ORIGINAL\n%v\nCURRENT\n%v",
   976  			string(originalYAML), string(current))
   977  	}
   978  }
   979  
   980  // Ensures that a no-op Update does not mutate managed fields
   981  func TestUpdateDoesNotChangeManagedFields(t *testing.T) {
   982  	originalManagedFields := []metav1.ManagedFieldsEntry{}
   983  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter,
   984  		schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
   985  	newObj := &unstructured.Unstructured{
   986  		Object: map[string]interface{}{},
   987  	}
   988  
   989  	if err := yaml.Unmarshal([]byte(`{
   990  		"apiVersion": "apps/v1",
   991  		"kind": "Deployment",
   992  		"metadata": {
   993  			"name": "deployment",
   994  			"labels": {"app": "nginx"}
   995  		},
   996  		"spec": {
   997  			"selector": {
   998  				"matchLabels": {
   999  					"app": "nginx"
  1000  				}
  1001  			},
  1002  			"template": {
  1003  				"metadata": {
  1004  					"labels": {
  1005  						"app": "nginx"
  1006  					}
  1007  				},
  1008  				"spec": {
  1009  					"containers": [{
  1010  						"name":  "nginx",
  1011  						"image": "nginx:latest"
  1012  					}]
  1013  				}
  1014  			}
  1015  		}
  1016  	}`), &newObj.Object); err != nil {
  1017  		t.Fatalf("error decoding YAML: %v", err)
  1018  	}
  1019  
  1020  	// Agent A updates with initial configuration
  1021  	if err := f.Update(newObj.DeepCopyObject(), "fieldmanager_z"); err != nil {
  1022  		t.Fatalf("failed to apply object: %v", err)
  1023  	}
  1024  
  1025  	for _, field := range f.ManagedFields() {
  1026  		originalManagedFields = append(originalManagedFields, *field.DeepCopy())
  1027  	}
  1028  
  1029  	// Make sure timestamp change would be caught
  1030  	time.Sleep(2 * time.Second)
  1031  
  1032  	// If the same exact configuration is updated once again, the
  1033  	// managed fields are not expected to change
  1034  	//
  1035  	// However, a change in field ownership WOULD be a semantic change which
  1036  	// should cause managed fields to change.
  1037  	if err := f.Update(newObj, "fieldmanager_z"); err != nil {
  1038  		t.Fatalf("failed to apply object: %v", err)
  1039  	}
  1040  
  1041  	// ensure that the live object is unchanged
  1042  	if !reflect.DeepEqual(originalManagedFields, f.ManagedFields()) {
  1043  		originalYAML, _ := yaml.Marshal(originalManagedFields)
  1044  		current, _ := yaml.Marshal(f.ManagedFields())
  1045  
  1046  		// should have been a no-op w.r.t. managed fields
  1047  		t.Fatalf("managed fields changed: ORIGINAL\n%v\nCURRENT\n%v",
  1048  			string(originalYAML), string(current))
  1049  	}
  1050  }
  1051  
  1052  // This test makes sure that the liveObject during a patch does not mutate
  1053  // its managed fields.
  1054  func TestLiveObjectManagedFieldsNotRemoved(t *testing.T) {
  1055  	f := managedfieldstest.NewTestFieldManager(fakeTypeConverter,
  1056  		schema.FromAPIVersionAndKind("apps/v1", "Deployment"))
  1057  	newObj := &unstructured.Unstructured{
  1058  		Object: map[string]interface{}{},
  1059  	}
  1060  	appliedObj := &unstructured.Unstructured{
  1061  		Object: map[string]interface{}{},
  1062  	}
  1063  	// Convert YAML string inputs to unstructured instances
  1064  	if err := yaml.Unmarshal([]byte(`{
  1065  		"apiVersion": "apps/v1",
  1066  		"kind": "Deployment",
  1067  		"metadata": {
  1068  			"name": "deployment",
  1069  			"labels": {"app": "nginx"}
  1070  		},
  1071  		"spec": {
  1072  			"selector": {
  1073  				"matchLabels": {
  1074  					"app": "nginx"
  1075  				}
  1076  			},
  1077  			"template": {
  1078  				"metadata": {
  1079  					"labels": {
  1080  						"app": "nginx"
  1081  					}
  1082  				},
  1083  				"spec": {
  1084  					"containers": [{
  1085  						"name":  "nginx",
  1086  						"image": "nginx:latest"
  1087  					}]
  1088  				}
  1089  			}
  1090  		}
  1091  	}`), &newObj.Object); err != nil {
  1092  		t.Fatalf("error decoding YAML: %v", err)
  1093  	}
  1094  
  1095  	if err := yaml.Unmarshal([]byte(`{
  1096  		"apiVersion": "apps/v1",
  1097  		"kind": "Deployment",
  1098  		"metadata": {
  1099  			"name": "deployment",
  1100  		},
  1101  		"spec": {
  1102  			"replicas": 101,
  1103  		}
  1104  	}`), &appliedObj.Object); err != nil {
  1105  		t.Fatalf("error decoding YAML: %v", err)
  1106  	}
  1107  
  1108  	// Agent A applies initial configuration
  1109  	if err := f.Apply(newObj.DeepCopyObject(), "fieldmanager_z", false); err != nil {
  1110  		t.Fatalf("failed to apply object: %v", err)
  1111  	}
  1112  
  1113  	originalLiveObj := f.Live()
  1114  
  1115  	accessor, err := meta.Accessor(originalLiveObj)
  1116  	if err != nil {
  1117  		panic(fmt.Errorf("couldn't get accessor: %v", err))
  1118  	}
  1119  
  1120  	// Managed fields should not be stripped
  1121  	if len(accessor.GetManagedFields()) == 0 {
  1122  		t.Fatalf("empty managed fields of object which expected nonzero fields")
  1123  	}
  1124  
  1125  	// Agent A applies the exact same configuration
  1126  	if err := f.Apply(appliedObj.DeepCopyObject(), "fieldmanager_z", false); err != nil {
  1127  		t.Fatalf("failed to apply object: %v", err)
  1128  	}
  1129  
  1130  	accessor, err = meta.Accessor(originalLiveObj)
  1131  	if err != nil {
  1132  		panic(fmt.Errorf("couldn't get accessor: %v", err))
  1133  	}
  1134  
  1135  	// Managed fields should not be stripped
  1136  	if len(accessor.GetManagedFields()) == 0 {
  1137  		t.Fatalf("empty managed fields of object which expected nonzero fields")
  1138  	}
  1139  }
  1140  

View as plain text