...

Source file src/k8s.io/kubernetes/test/integration/apiserver/apply/apply_test.go

Documentation: k8s.io/kubernetes/test/integration/apiserver/apply

     1  /*
     2  Copyright 2018 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 apiserver
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"flag"
    24  	"fmt"
    25  	"net/http"
    26  	"reflect"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/google/go-cmp/cmp"
    32  	"github.com/stretchr/testify/require"
    33  	"sigs.k8s.io/yaml"
    34  
    35  	appsv1 "k8s.io/api/apps/v1"
    36  	v1 "k8s.io/api/core/v1"
    37  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    38  	"k8s.io/apimachinery/pkg/api/meta"
    39  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    40  	"k8s.io/apimachinery/pkg/runtime"
    41  	"k8s.io/apimachinery/pkg/types"
    42  	"k8s.io/apimachinery/pkg/util/wait"
    43  	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
    44  	clientset "k8s.io/client-go/kubernetes"
    45  	restclient "k8s.io/client-go/rest"
    46  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    47  	"k8s.io/kubernetes/test/integration/framework"
    48  )
    49  
    50  func setup(t testing.TB) (clientset.Interface, kubeapiservertesting.TearDownFunc) {
    51  	// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
    52  	server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
    53  
    54  	config := restclient.CopyConfig(server.ClientConfig)
    55  	// There are some tests (in scale_test.go) that rely on the response to be returned in JSON.
    56  	// So we overwrite it here.
    57  	config.ContentType = runtime.ContentTypeJSON
    58  	clientSet, err := clientset.NewForConfig(config)
    59  	if err != nil {
    60  		t.Fatalf("Error in create clientset: %v", err)
    61  	}
    62  	return clientSet, server.TearDownFn
    63  }
    64  
    65  // TestApplyAlsoCreates makes sure that PATCH requests with the apply content type
    66  // will create the object if it doesn't already exist
    67  // TODO: make a set of test cases in an easy-to-consume place (separate package?) so it's easy to test in both integration and e2e.
    68  func TestApplyAlsoCreates(t *testing.T) {
    69  	client, closeFn := setup(t)
    70  	defer closeFn()
    71  
    72  	testCases := []struct {
    73  		resource string
    74  		name     string
    75  		body     string
    76  	}{
    77  		{
    78  			resource: "pods",
    79  			name:     "test-pod",
    80  			body: `{
    81  				"apiVersion": "v1",
    82  				"kind": "Pod",
    83  				"metadata": {
    84  					"name": "test-pod"
    85  				},
    86  				"spec": {
    87  					"containers": [{
    88  						"name":  "test-container",
    89  						"image": "test-image"
    90  					}]
    91  				}
    92  			}`,
    93  		}, {
    94  			resource: "services",
    95  			name:     "test-svc",
    96  			body: `{
    97  				"apiVersion": "v1",
    98  				"kind": "Service",
    99  				"metadata": {
   100  					"name": "test-svc"
   101  				},
   102  				"spec": {
   103  					"ports": [{
   104  						"port": 8080,
   105  						"protocol": "UDP"
   106  					}]
   107  				}
   108  			}`,
   109  		},
   110  	}
   111  
   112  	for _, tc := range testCases {
   113  		t.Run(tc.name, func(t *testing.T) {
   114  			_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   115  				Namespace("default").
   116  				Resource(tc.resource).
   117  				Name(tc.name).
   118  				Param("fieldManager", "apply_test").
   119  				Body([]byte(tc.body)).
   120  				Do(context.TODO()).
   121  				Get()
   122  			if err != nil {
   123  				t.Fatalf("Failed to create object using Apply patch: %v", err)
   124  			}
   125  
   126  			_, err = client.CoreV1().RESTClient().Get().Namespace("default").Resource(tc.resource).Name(tc.name).Do(context.TODO()).Get()
   127  			if err != nil {
   128  				t.Fatalf("Failed to retrieve object: %v", err)
   129  			}
   130  
   131  			// Test that we can re apply with a different field manager and don't get conflicts
   132  			_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   133  				Namespace("default").
   134  				Resource(tc.resource).
   135  				Name(tc.name).
   136  				Param("fieldManager", "apply_test_2").
   137  				Body([]byte(tc.body)).
   138  				Do(context.TODO()).
   139  				Get()
   140  			if err != nil {
   141  				t.Fatalf("Failed to re-apply object using Apply patch: %v", err)
   142  			}
   143  		})
   144  	}
   145  }
   146  
   147  // TestNoOpUpdateSameResourceVersion makes sure that PUT requests which change nothing
   148  // will not change the resource version (no write to etcd is done)
   149  func TestNoOpUpdateSameResourceVersion(t *testing.T) {
   150  	client, closeFn := setup(t)
   151  	defer closeFn()
   152  
   153  	podName := "no-op"
   154  	podResource := "pods"
   155  	podBytes := []byte(`{
   156  		"apiVersion": "v1",
   157  		"kind": "Pod",
   158  		"metadata": {
   159  			"name": "` + podName + `",
   160  			"labels": {
   161  				"a": "one",
   162  				"c": "two",
   163  				"b": "three"
   164  			}
   165  		},
   166  		"spec": {
   167  			"containers": [{
   168  				"name":  "test-container-a",
   169  				"image": "test-image-one"
   170  			},{
   171  				"name":  "test-container-c",
   172  				"image": "test-image-two"
   173  			},{
   174  				"name":  "test-container-b",
   175  				"image": "test-image-three"
   176  			}]
   177  		}
   178  	}`)
   179  
   180  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   181  		Namespace("default").
   182  		Param("fieldManager", "apply_test").
   183  		Resource(podResource).
   184  		Name(podName).
   185  		Body(podBytes).
   186  		Do(context.TODO()).
   187  		Get()
   188  	if err != nil {
   189  		t.Fatalf("Failed to create object: %v", err)
   190  	}
   191  
   192  	// Sleep for one second to make sure that the times of each update operation is different.
   193  	time.Sleep(1 * time.Second)
   194  
   195  	createdObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
   196  	if err != nil {
   197  		t.Fatalf("Failed to retrieve created object: %v", err)
   198  	}
   199  
   200  	createdAccessor, err := meta.Accessor(createdObject)
   201  	if err != nil {
   202  		t.Fatalf("Failed to get meta accessor for created object: %v", err)
   203  	}
   204  
   205  	createdBytes, err := json.MarshalIndent(createdObject, "\t", "\t")
   206  	if err != nil {
   207  		t.Fatalf("Failed to marshal created object: %v", err)
   208  	}
   209  
   210  	// Test that we can put the same object and don't change the RV
   211  	_, err = client.CoreV1().RESTClient().Put().
   212  		Namespace("default").
   213  		Resource(podResource).
   214  		Name(podName).
   215  		Body(createdBytes).
   216  		Do(context.TODO()).
   217  		Get()
   218  	if err != nil {
   219  		t.Fatalf("Failed to apply no-op update: %v", err)
   220  	}
   221  
   222  	updatedObject, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource(podResource).Name(podName).Do(context.TODO()).Get()
   223  	if err != nil {
   224  		t.Fatalf("Failed to retrieve updated object: %v", err)
   225  	}
   226  
   227  	updatedAccessor, err := meta.Accessor(updatedObject)
   228  	if err != nil {
   229  		t.Fatalf("Failed to get meta accessor for updated object: %v", err)
   230  	}
   231  
   232  	updatedBytes, err := json.MarshalIndent(updatedObject, "\t", "\t")
   233  	if err != nil {
   234  		t.Fatalf("Failed to marshal updated object: %v", err)
   235  	}
   236  
   237  	if createdAccessor.GetResourceVersion() != updatedAccessor.GetResourceVersion() {
   238  		t.Fatalf("Expected same resource version to be %v but got: %v\nold object:\n%v\nnew object:\n%v",
   239  			createdAccessor.GetResourceVersion(),
   240  			updatedAccessor.GetResourceVersion(),
   241  			string(createdBytes),
   242  			string(updatedBytes),
   243  		)
   244  	}
   245  }
   246  
   247  func getRV(obj runtime.Object) (string, error) {
   248  	acc, err := meta.Accessor(obj)
   249  	if err != nil {
   250  		return "", err
   251  	}
   252  	return acc.GetResourceVersion(), nil
   253  }
   254  
   255  func TestNoopChangeCreationTime(t *testing.T) {
   256  	client, closeFn := setup(t)
   257  	defer closeFn()
   258  
   259  	ssBytes := []byte(`{
   260  		"apiVersion": "v1",
   261  		"kind": "ConfigMap",
   262  		"metadata": {
   263  			"name": "myconfig",
   264  			"creationTimestamp": null,
   265  			"resourceVersion": null
   266  		},
   267  		"data": {
   268  			"key": "value"
   269  		}
   270  	}`)
   271  
   272  	obj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   273  		Namespace("default").
   274  		Param("fieldManager", "apply_test").
   275  		Resource("configmaps").
   276  		Name("myconfig").
   277  		Body(ssBytes).
   278  		Do(context.TODO()).
   279  		Get()
   280  	if err != nil {
   281  		t.Fatalf("Failed to create object: %v", err)
   282  	}
   283  
   284  	require.NoError(t, err)
   285  	// Sleep for one second to make sure that the times of each update operation is different.
   286  	time.Sleep(1200 * time.Millisecond)
   287  
   288  	newObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   289  		Namespace("default").
   290  		Param("fieldManager", "apply_test").
   291  		Resource("configmaps").
   292  		Name("myconfig").
   293  		Body(ssBytes).
   294  		Do(context.TODO()).
   295  		Get()
   296  	if err != nil {
   297  		t.Fatalf("Failed to create object: %v", err)
   298  	}
   299  
   300  	require.NoError(t, err)
   301  	require.Equal(t, obj, newObj)
   302  }
   303  
   304  // TestNoSemanticUpdateAppleSameResourceVersion makes sure that APPLY requests which makes no semantic changes
   305  // will not change the resource version (no write to etcd is done)
   306  //
   307  // Some of the non-semantic changes are:
   308  // - Applying an atomic struct that removes a default
   309  // - Changing Quantity or other fields that are normalized
   310  func TestNoSemanticUpdateApplySameResourceVersion(t *testing.T) {
   311  	client, closeFn := setup(t)
   312  	defer closeFn()
   313  
   314  	ssBytes := []byte(`{
   315  		"apiVersion": "apps/v1",
   316  		"kind": "StatefulSet",
   317  		"metadata": {
   318  			"name": "nginx",
   319  			"labels": {"app": "nginx"}
   320  		},
   321  		"spec": {
   322  			"serviceName": "nginx",
   323  			"selector": { "matchLabels": {"app": "nginx"}},
   324  			"template": {
   325  				"metadata": {
   326  					"labels": {"app": "nginx"}
   327  				},
   328  				"spec": {
   329  					"containers": [{
   330  						"name":  "nginx",
   331  						"image": "nginx",
   332  						"resources": {
   333  							"limits": {"memory": "2048Mi"}
   334  						}
   335  					}]
   336  				}
   337  			},
   338  			"volumeClaimTemplates": [{
   339  				"metadata": {"name": "nginx"},
   340  				"spec": {
   341  					"accessModes": ["ReadWriteOnce"],
   342  					"resources": {"requests": {"storage": "1Gi"}}
   343  				}
   344  			}]
   345  		}
   346  	}`)
   347  
   348  	obj, err := client.AppsV1().RESTClient().Patch(types.ApplyPatchType).
   349  		Namespace("default").
   350  		Param("fieldManager", "apply_test").
   351  		Resource("statefulsets").
   352  		Name("nginx").
   353  		Body(ssBytes).
   354  		Do(context.TODO()).
   355  		Get()
   356  	if err != nil {
   357  		t.Fatalf("Failed to create object: %v", err)
   358  	}
   359  
   360  	rvCreated, err := getRV(obj)
   361  	if err != nil {
   362  		t.Fatalf("Failed to get RV: %v", err)
   363  	}
   364  
   365  	// Sleep for one second to make sure that the times of each update operation is different.
   366  	time.Sleep(1200 * time.Millisecond)
   367  
   368  	obj, err = client.AppsV1().RESTClient().Patch(types.ApplyPatchType).
   369  		Namespace("default").
   370  		Param("fieldManager", "apply_test").
   371  		Resource("statefulsets").
   372  		Name("nginx").
   373  		Body(ssBytes).
   374  		Do(context.TODO()).
   375  		Get()
   376  	if err != nil {
   377  		t.Fatalf("Failed to create object: %v", err)
   378  	}
   379  	rvApplied, err := getRV(obj)
   380  	if err != nil {
   381  		t.Fatalf("Failed to get RV: %v", err)
   382  	}
   383  	if rvApplied != rvCreated {
   384  		t.Fatal("ResourceVersion changed after apply")
   385  	}
   386  }
   387  
   388  // TestNoSemanticUpdateAppleSameResourceVersion makes sure that PUT requests which makes no semantic changes
   389  // will not change the resource version (no write to etcd is done)
   390  //
   391  // Some of the non-semantic changes are:
   392  // - Applying an atomic struct that removes a default
   393  // - Changing Quantity or other fields that are normalized
   394  func TestNoSemanticUpdatePutSameResourceVersion(t *testing.T) {
   395  	client, closeFn := setup(t)
   396  	defer closeFn()
   397  
   398  	ssBytes := []byte(`{
   399  		"apiVersion": "apps/v1",
   400  		"kind": "StatefulSet",
   401  		"metadata": {
   402  			"name": "nginx",
   403  			"labels": {"app": "nginx"}
   404  		},
   405  		"spec": {
   406  			"serviceName": "nginx",
   407  			"selector": { "matchLabels": {"app": "nginx"}},
   408  			"template": {
   409  				"metadata": {
   410  					"labels": {"app": "nginx"}
   411  				},
   412  				"spec": {
   413  					"containers": [{
   414  						"name":  "nginx",
   415  						"image": "nginx",
   416  						"resources": {
   417  							"limits": {"memory": "2048Mi"}
   418  						}
   419  					}]
   420  				}
   421  			},
   422  			"volumeClaimTemplates": [{
   423  				"metadata": {"name": "nginx"},
   424  				"spec": {
   425  					"accessModes": ["ReadWriteOnce"],
   426  					"resources": { "requests": { "storage": "1Gi"}}
   427  				}
   428  			}]
   429  		}
   430  	}`)
   431  
   432  	obj, err := client.AppsV1().RESTClient().Post().
   433  		Namespace("default").
   434  		Param("fieldManager", "apply_test").
   435  		Resource("statefulsets").
   436  		Body(ssBytes).
   437  		Do(context.TODO()).
   438  		Get()
   439  	if err != nil {
   440  		t.Fatalf("Failed to create object: %v", err)
   441  	}
   442  
   443  	rvCreated, err := getRV(obj)
   444  	if err != nil {
   445  		t.Fatalf("Failed to get RV: %v", err)
   446  	}
   447  
   448  	// Sleep for one second to make sure that the times of each update operation is different.
   449  	time.Sleep(1200 * time.Millisecond)
   450  
   451  	obj, err = client.AppsV1().RESTClient().Put().
   452  		Namespace("default").
   453  		Param("fieldManager", "apply_test").
   454  		Resource("statefulsets").
   455  		Name("nginx").
   456  		Body(ssBytes).
   457  		Do(context.TODO()).
   458  		Get()
   459  	if err != nil {
   460  		t.Fatalf("Failed to create object: %v", err)
   461  	}
   462  	rvApplied, err := getRV(obj)
   463  	if err != nil {
   464  		t.Fatalf("Failed to get RV: %v", err)
   465  	}
   466  	if rvApplied != rvCreated {
   467  		t.Fatal("ResourceVersion changed after similar PUT")
   468  	}
   469  }
   470  
   471  // TestCreateOnApplyFailsWithUID makes sure that PATCH requests with the apply content type
   472  // will not create the object if it doesn't already exist and it specifies a UID
   473  func TestCreateOnApplyFailsWithUID(t *testing.T) {
   474  	client, closeFn := setup(t)
   475  	defer closeFn()
   476  
   477  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   478  		Namespace("default").
   479  		Resource("pods").
   480  		Name("test-pod-uid").
   481  		Param("fieldManager", "apply_test").
   482  		Body([]byte(`{
   483  			"apiVersion": "v1",
   484  			"kind": "Pod",
   485  			"metadata": {
   486  				"name": "test-pod-uid",
   487  				"uid":  "88e00824-7f0e-11e8-94a1-c8d3ffb15800"
   488  			},
   489  			"spec": {
   490  				"containers": [{
   491  					"name":  "test-container",
   492  					"image": "test-image"
   493  				}]
   494  			}
   495  		}`)).
   496  		Do(context.TODO()).
   497  		Get()
   498  	if !apierrors.IsConflict(err) {
   499  		t.Fatalf("Expected conflict error but got: %v", err)
   500  	}
   501  }
   502  
   503  func TestApplyUpdateApplyConflictForced(t *testing.T) {
   504  	client, closeFn := setup(t)
   505  	defer closeFn()
   506  
   507  	obj := []byte(`{
   508  		"apiVersion": "apps/v1",
   509  		"kind": "Deployment",
   510  		"metadata": {
   511  			"name": "deployment",
   512  			"labels": {"app": "nginx"}
   513  		},
   514  		"spec": {
   515  			"replicas": 3,
   516  			"selector": {
   517  				"matchLabels": {
   518  					 "app": "nginx"
   519  				}
   520  			},
   521  			"template": {
   522  				"metadata": {
   523  					"labels": {
   524  						"app": "nginx"
   525  					}
   526  				},
   527  				"spec": {
   528  					"containers": [{
   529  						"name":  "nginx",
   530  						"image": "nginx:latest"
   531  					}]
   532  				}
   533  			}
   534  		}
   535  	}`)
   536  
   537  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   538  		AbsPath("/apis/apps/v1").
   539  		Namespace("default").
   540  		Resource("deployments").
   541  		Name("deployment").
   542  		Param("fieldManager", "apply_test").
   543  		Body(obj).Do(context.TODO()).Get()
   544  	if err != nil {
   545  		t.Fatalf("Failed to create object using Apply patch: %v", err)
   546  	}
   547  
   548  	_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
   549  		AbsPath("/apis/apps/v1").
   550  		Namespace("default").
   551  		Resource("deployments").
   552  		Name("deployment").
   553  		Body([]byte(`{"spec":{"replicas": 5}}`)).Do(context.TODO()).Get()
   554  	if err != nil {
   555  		t.Fatalf("Failed to patch object: %v", err)
   556  	}
   557  
   558  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   559  		AbsPath("/apis/apps/v1").
   560  		Namespace("default").
   561  		Resource("deployments").
   562  		Name("deployment").
   563  		Param("fieldManager", "apply_test").
   564  		Body([]byte(obj)).Do(context.TODO()).Get()
   565  	if err == nil {
   566  		t.Fatalf("Expecting to get conflicts when applying object")
   567  	}
   568  	status, ok := err.(*apierrors.StatusError)
   569  	if !ok {
   570  		t.Fatalf("Expecting to get conflicts as API error")
   571  	}
   572  	if len(status.Status().Details.Causes) < 1 {
   573  		t.Fatalf("Expecting to get at least one conflict when applying object, got: %v", status.Status().Details.Causes)
   574  	}
   575  
   576  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   577  		AbsPath("/apis/apps/v1").
   578  		Namespace("default").
   579  		Resource("deployments").
   580  		Name("deployment").
   581  		Param("force", "true").
   582  		Param("fieldManager", "apply_test").
   583  		Body([]byte(obj)).Do(context.TODO()).Get()
   584  	if err != nil {
   585  		t.Fatalf("Failed to apply object with force: %v", err)
   586  	}
   587  }
   588  
   589  // TestApplyGroupsManySeparateUpdates tests that when many different managers update the same object,
   590  // the number of managedFields entries will only grow to a certain size.
   591  func TestApplyGroupsManySeparateUpdates(t *testing.T) {
   592  	client, closeFn := setup(t)
   593  	defer closeFn()
   594  
   595  	obj := []byte(`{
   596  		"apiVersion": "admissionregistration.k8s.io/v1",
   597  		"kind": "ValidatingWebhookConfiguration",
   598  		"metadata": {
   599  			"name": "webhook",
   600  			"labels": {"applier":"true"},
   601  		},
   602  	}`)
   603  
   604  	object, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   605  		AbsPath("/apis/admissionregistration.k8s.io/v1").
   606  		Resource("validatingwebhookconfigurations").
   607  		Name("webhook").
   608  		Param("fieldManager", "apply_test").
   609  		Body(obj).Do(context.TODO()).Get()
   610  	if err != nil {
   611  		t.Fatalf("Failed to create object using Apply patch: %v", err)
   612  	}
   613  
   614  	for i := 0; i < 20; i++ {
   615  		unique := fmt.Sprintf("updater%v", i)
   616  		object, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
   617  			AbsPath("/apis/admissionregistration.k8s.io/v1").
   618  			Resource("validatingwebhookconfigurations").
   619  			Name("webhook").
   620  			Param("fieldManager", unique).
   621  			Body([]byte(`{"metadata":{"labels":{"` + unique + `":"new"}}}`)).Do(context.TODO()).Get()
   622  		if err != nil {
   623  			t.Fatalf("Failed to patch object: %v", err)
   624  		}
   625  	}
   626  
   627  	accessor, err := meta.Accessor(object)
   628  	if err != nil {
   629  		t.Fatalf("Failed to get meta accessor: %v", err)
   630  	}
   631  
   632  	// Expect 11 entries, because the cap for update entries is 10, and 1 apply entry
   633  	if actual, expected := len(accessor.GetManagedFields()), 11; actual != expected {
   634  		if b, err := json.MarshalIndent(object, "\t", "\t"); err == nil {
   635  			t.Fatalf("Object expected to contain %v entries in managedFields, but got %v:\n%v", expected, actual, string(b))
   636  		} else {
   637  			t.Fatalf("Object expected to contain %v entries in managedFields, but got %v: error marshalling object: %v", expected, actual, err)
   638  		}
   639  	}
   640  
   641  	// Expect the first entry to have the manager name "apply_test"
   642  	if actual, expected := accessor.GetManagedFields()[0].Manager, "apply_test"; actual != expected {
   643  		t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
   644  	}
   645  
   646  	// Expect the second entry to have the manager name "ancient-changes"
   647  	if actual, expected := accessor.GetManagedFields()[1].Manager, "ancient-changes"; actual != expected {
   648  		t.Fatalf("Expected first manager to be named %v but got %v", expected, actual)
   649  	}
   650  }
   651  
   652  // TestCreateVeryLargeObject tests that a very large object can be created without exceeding the size limit due to managedFields
   653  func TestCreateVeryLargeObject(t *testing.T) {
   654  	client, closeFn := setup(t)
   655  	defer closeFn()
   656  
   657  	cfg := &v1.ConfigMap{
   658  		ObjectMeta: metav1.ObjectMeta{
   659  			Name:      "large-create-test-cm",
   660  			Namespace: "default",
   661  		},
   662  		Data: map[string]string{},
   663  	}
   664  
   665  	for i := 0; i < 9999; i++ {
   666  		unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
   667  		cfg.Data[unique] = "A"
   668  	}
   669  
   670  	// Should be able to create an object near the object size limit.
   671  	if _, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{}); err != nil {
   672  		t.Errorf("unable to create large test configMap: %v", err)
   673  	}
   674  
   675  	// Applying to the same object should cause managedFields to go over the object size limit, and fail.
   676  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   677  		Namespace(cfg.Namespace).
   678  		Resource("configmaps").
   679  		Name(cfg.Name).
   680  		Param("fieldManager", "apply_test").
   681  		Body([]byte(`{
   682  			"apiVersion": "v1",
   683  			"kind": "ConfigMap",
   684  			"metadata": {
   685  				"name": "large-create-test-cm",
   686  				"namespace": "default",
   687  			}
   688  		}`)).
   689  		Do(context.TODO()).
   690  		Get()
   691  	if err == nil {
   692  		t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
   693  	}
   694  }
   695  
   696  // TestUpdateVeryLargeObject tests that a small object can be updated to be very large without exceeding the size limit due to managedFields
   697  func TestUpdateVeryLargeObject(t *testing.T) {
   698  	client, closeFn := setup(t)
   699  	defer closeFn()
   700  
   701  	cfg := &v1.ConfigMap{
   702  		ObjectMeta: metav1.ObjectMeta{
   703  			Name:      "large-update-test-cm",
   704  			Namespace: "default",
   705  		},
   706  		Data: map[string]string{"k": "v"},
   707  	}
   708  
   709  	// Create a small config map.
   710  	cfg, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{})
   711  	if err != nil {
   712  		t.Errorf("unable to create configMap: %v", err)
   713  	}
   714  
   715  	// Should be able to update a small object to be near the object size limit.
   716  	var updateErr error
   717  	pollErr := wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) {
   718  		updateCfg, err := client.CoreV1().ConfigMaps(cfg.Namespace).Get(context.TODO(), cfg.Name, metav1.GetOptions{})
   719  		if err != nil {
   720  			return false, err
   721  		}
   722  
   723  		// Apply the large update, then attempt to push it to the apiserver.
   724  		for i := 0; i < 9999; i++ {
   725  			unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
   726  			updateCfg.Data[unique] = "A"
   727  		}
   728  
   729  		if _, err = client.CoreV1().ConfigMaps(cfg.Namespace).Update(context.TODO(), updateCfg, metav1.UpdateOptions{}); err == nil {
   730  			return true, nil
   731  		}
   732  		updateErr = err
   733  		return false, nil
   734  	})
   735  	if pollErr == wait.ErrWaitTimeout {
   736  		t.Errorf("unable to update configMap: %v", updateErr)
   737  	}
   738  
   739  	// Applying to the same object should cause managedFields to go over the object size limit, and fail.
   740  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   741  		Namespace(cfg.Namespace).
   742  		Resource("configmaps").
   743  		Name(cfg.Name).
   744  		Param("fieldManager", "apply_test").
   745  		Body([]byte(`{
   746  			"apiVersion": "v1",
   747  			"kind": "ConfigMap",
   748  			"metadata": {
   749  				"name": "large-update-test-cm",
   750  				"namespace": "default",
   751  			}
   752  		}`)).
   753  		Do(context.TODO()).
   754  		Get()
   755  	if err == nil {
   756  		t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
   757  	}
   758  }
   759  
   760  // TestPatchVeryLargeObject tests that a small object can be patched to be very large without exceeding the size limit due to managedFields
   761  func TestPatchVeryLargeObject(t *testing.T) {
   762  	client, closeFn := setup(t)
   763  	defer closeFn()
   764  
   765  	cfg := &v1.ConfigMap{
   766  		ObjectMeta: metav1.ObjectMeta{
   767  			Name:      "large-patch-test-cm",
   768  			Namespace: "default",
   769  		},
   770  		Data: map[string]string{"k": "v"},
   771  	}
   772  
   773  	// Create a small config map.
   774  	if _, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(context.TODO(), cfg, metav1.CreateOptions{}); err != nil {
   775  		t.Errorf("unable to create configMap: %v", err)
   776  	}
   777  
   778  	patchString := `{"data":{"k":"v"`
   779  	for i := 0; i < 9999; i++ {
   780  		unique := fmt.Sprintf("this-key-is-very-long-so-as-to-create-a-very-large-serialized-fieldset-%v", i)
   781  		patchString = fmt.Sprintf("%s,%q:%q", patchString, unique, "A")
   782  	}
   783  	patchString = fmt.Sprintf("%s}}", patchString)
   784  
   785  	// Should be able to update a small object to be near the object size limit.
   786  	_, err := client.CoreV1().RESTClient().Patch(types.MergePatchType).
   787  		AbsPath("/api/v1").
   788  		Namespace(cfg.Namespace).
   789  		Resource("configmaps").
   790  		Name(cfg.Name).
   791  		Body([]byte(patchString)).Do(context.TODO()).Get()
   792  	if err != nil {
   793  		t.Errorf("unable to patch configMap: %v", err)
   794  	}
   795  
   796  	// Applying to the same object should cause managedFields to go over the object size limit, and fail.
   797  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   798  		Namespace("default").
   799  		Resource("configmaps").
   800  		Name("large-patch-test-cm").
   801  		Param("fieldManager", "apply_test").
   802  		Body([]byte(`{
   803  			"apiVersion": "v1",
   804  			"kind": "ConfigMap",
   805  			"metadata": {
   806  				"name": "large-patch-test-cm",
   807  				"namespace": "default",
   808  			}
   809  		}`)).
   810  		Do(context.TODO()).
   811  		Get()
   812  	if err == nil {
   813  		t.Fatalf("expected to fail to update object using Apply patch, but succeeded")
   814  	}
   815  }
   816  
   817  // TestApplyManagedFields makes sure that managedFields api does not change
   818  func TestApplyManagedFields(t *testing.T) {
   819  	client, closeFn := setup(t)
   820  	defer closeFn()
   821  
   822  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   823  		Namespace("default").
   824  		Resource("configmaps").
   825  		Name("test-cm").
   826  		Param("fieldManager", "apply_test").
   827  		Body([]byte(`{
   828  			"apiVersion": "v1",
   829  			"kind": "ConfigMap",
   830  			"metadata": {
   831  				"name": "test-cm",
   832  				"namespace": "default",
   833  				"labels": {
   834  					"test-label": "test"
   835  				}
   836  			},
   837  			"data": {
   838  				"key": "value"
   839  			}
   840  		}`)).
   841  		Do(context.TODO()).
   842  		Get()
   843  	if err != nil {
   844  		t.Fatalf("Failed to create object using Apply patch: %v", err)
   845  	}
   846  
   847  	_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
   848  		Namespace("default").
   849  		Resource("configmaps").
   850  		Name("test-cm").
   851  		Param("fieldManager", "updater").
   852  		Body([]byte(`{"data":{"new-key": "value"}}`)).Do(context.TODO()).Get()
   853  	if err != nil {
   854  		t.Fatalf("Failed to patch object: %v", err)
   855  	}
   856  
   857  	// Sleep for one second to make sure that the times of each update operation is different.
   858  	// This will let us check that update entries with the same manager name are grouped together,
   859  	// and that the most recent update time is recorded in the grouped entry.
   860  	time.Sleep(1 * time.Second)
   861  
   862  	_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
   863  		Namespace("default").
   864  		Resource("configmaps").
   865  		Name("test-cm").
   866  		Param("fieldManager", "updater").
   867  		Body([]byte(`{"data":{"key": "new value"}}`)).Do(context.TODO()).Get()
   868  	if err != nil {
   869  		t.Fatalf("Failed to patch object: %v", err)
   870  	}
   871  
   872  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
   873  	if err != nil {
   874  		t.Fatalf("Failed to retrieve object: %v", err)
   875  	}
   876  
   877  	accessor, err := meta.Accessor(object)
   878  	if err != nil {
   879  		t.Fatalf("Failed to get meta accessor: %v", err)
   880  	}
   881  
   882  	actual, err := json.MarshalIndent(object, "\t", "\t")
   883  	if err != nil {
   884  		t.Fatalf("Failed to marshal object: %v", err)
   885  	}
   886  
   887  	expected := []byte(`{
   888  		"metadata": {
   889  			"name": "test-cm",
   890  			"namespace": "default",
   891  			"uid": "` + string(accessor.GetUID()) + `",
   892  			"resourceVersion": "` + accessor.GetResourceVersion() + `",
   893  			"creationTimestamp": "` + accessor.GetCreationTimestamp().UTC().Format(time.RFC3339) + `",
   894  			"labels": {
   895  				"test-label": "test"
   896  			},
   897  			"managedFields": [
   898  				{
   899  					"manager": "apply_test",
   900  					"operation": "Apply",
   901  					"apiVersion": "v1",
   902  					"time": "` + accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) + `",
   903  					"fieldsType": "FieldsV1",
   904  					"fieldsV1": {
   905  						"f:metadata": {
   906  							"f:labels": {
   907  								"f:test-label": {}
   908  							}
   909  						}
   910  					}
   911  				},
   912  				{
   913  					"manager": "updater",
   914  					"operation": "Update",
   915  					"apiVersion": "v1",
   916  					"time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `",
   917  					"fieldsType": "FieldsV1",
   918  					"fieldsV1": {
   919  						"f:data": {
   920  							"f:key": {},
   921  							"f:new-key": {}
   922  						}
   923  					}
   924  				}
   925  			]
   926  		},
   927  		"data": {
   928  			"key": "new value",
   929  			"new-key": "value"
   930  		}
   931  	}`)
   932  
   933  	if string(expected) != string(actual) {
   934  		t.Fatalf("Expected:\n%v\nGot:\n%v", string(expected), string(actual))
   935  	}
   936  
   937  	if accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) == accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) {
   938  		t.Fatalf("Expected times to be different but got:\n%v", string(actual))
   939  	}
   940  }
   941  
   942  // TestApplyRemovesEmptyManagedFields there are no empty managers in managedFields
   943  func TestApplyRemovesEmptyManagedFields(t *testing.T) {
   944  	client, closeFn := setup(t)
   945  	defer closeFn()
   946  
   947  	obj := []byte(`{
   948  		"apiVersion": "v1",
   949  		"kind": "ConfigMap",
   950  		"metadata": {
   951  			"name": "test-cm",
   952  			"namespace": "default"
   953  		}
   954  	}`)
   955  
   956  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   957  		Namespace("default").
   958  		Resource("configmaps").
   959  		Name("test-cm").
   960  		Param("fieldManager", "apply_test").
   961  		Body(obj).
   962  		Do(context.TODO()).
   963  		Get()
   964  	if err != nil {
   965  		t.Fatalf("Failed to create object using Apply patch: %v", err)
   966  	}
   967  
   968  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
   969  		Namespace("default").
   970  		Resource("configmaps").
   971  		Name("test-cm").
   972  		Param("fieldManager", "apply_test").
   973  		Body(obj).Do(context.TODO()).Get()
   974  	if err != nil {
   975  		t.Fatalf("Failed to patch object: %v", err)
   976  	}
   977  
   978  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
   979  	if err != nil {
   980  		t.Fatalf("Failed to retrieve object: %v", err)
   981  	}
   982  
   983  	accessor, err := meta.Accessor(object)
   984  	if err != nil {
   985  		t.Fatalf("Failed to get meta accessor: %v", err)
   986  	}
   987  
   988  	if managed := accessor.GetManagedFields(); managed != nil {
   989  		t.Fatalf("Object contains unexpected managedFields: %v", managed)
   990  	}
   991  }
   992  
   993  func TestApplyRequiresFieldManager(t *testing.T) {
   994  	client, closeFn := setup(t)
   995  	defer closeFn()
   996  
   997  	obj := []byte(`{
   998  		"apiVersion": "v1",
   999  		"kind": "ConfigMap",
  1000  		"metadata": {
  1001  			"name": "test-cm",
  1002  			"namespace": "default"
  1003  		}
  1004  	}`)
  1005  
  1006  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1007  		Namespace("default").
  1008  		Resource("configmaps").
  1009  		Name("test-cm").
  1010  		Body(obj).
  1011  		Do(context.TODO()).
  1012  		Get()
  1013  	if err == nil {
  1014  		t.Fatalf("Apply should fail to create without fieldManager")
  1015  	}
  1016  
  1017  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1018  		Namespace("default").
  1019  		Resource("configmaps").
  1020  		Name("test-cm").
  1021  		Param("fieldManager", "apply_test").
  1022  		Body(obj).
  1023  		Do(context.TODO()).
  1024  		Get()
  1025  	if err != nil {
  1026  		t.Fatalf("Apply failed to create with fieldManager: %v", err)
  1027  	}
  1028  }
  1029  
  1030  // TestApplyRemoveContainerPort removes a container port from a deployment
  1031  func TestApplyRemoveContainerPort(t *testing.T) {
  1032  	client, closeFn := setup(t)
  1033  	defer closeFn()
  1034  
  1035  	obj := []byte(`{
  1036  		"apiVersion": "apps/v1",
  1037  		"kind": "Deployment",
  1038  		"metadata": {
  1039  			"name": "deployment",
  1040  			"labels": {"app": "nginx"}
  1041  		},
  1042  		"spec": {
  1043  			"replicas": 3,
  1044  			"selector": {
  1045  				"matchLabels": {
  1046  					"app": "nginx"
  1047  				}
  1048  			},
  1049  			"template": {
  1050  				"metadata": {
  1051  					"labels": {
  1052  						"app": "nginx"
  1053  					}
  1054  				},
  1055  				"spec": {
  1056  					"containers": [{
  1057  						"name":  "nginx",
  1058  						"image": "nginx:latest",
  1059  						"ports": [{
  1060  							"containerPort": 80,
  1061  							"protocol": "TCP"
  1062  						}]
  1063  					}]
  1064  				}
  1065  			}
  1066  		}
  1067  	}`)
  1068  
  1069  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1070  		AbsPath("/apis/apps/v1").
  1071  		Namespace("default").
  1072  		Resource("deployments").
  1073  		Name("deployment").
  1074  		Param("fieldManager", "apply_test").
  1075  		Body(obj).Do(context.TODO()).Get()
  1076  	if err != nil {
  1077  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1078  	}
  1079  
  1080  	obj = []byte(`{
  1081  		"apiVersion": "apps/v1",
  1082  		"kind": "Deployment",
  1083  		"metadata": {
  1084  			"name": "deployment",
  1085  			"labels": {"app": "nginx"}
  1086  		},
  1087  		"spec": {
  1088  			"replicas": 3,
  1089  			"selector": {
  1090  				"matchLabels": {
  1091  					"app": "nginx"
  1092  				}
  1093  			},
  1094  			"template": {
  1095  				"metadata": {
  1096  					"labels": {
  1097  						"app": "nginx"
  1098  					}
  1099  				},
  1100  				"spec": {
  1101  					"containers": [{
  1102  						"name":  "nginx",
  1103  						"image": "nginx:latest"
  1104  					}]
  1105  				}
  1106  			}
  1107  		}
  1108  	}`)
  1109  
  1110  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1111  		AbsPath("/apis/apps/v1").
  1112  		Namespace("default").
  1113  		Resource("deployments").
  1114  		Name("deployment").
  1115  		Param("fieldManager", "apply_test").
  1116  		Body(obj).Do(context.TODO()).Get()
  1117  	if err != nil {
  1118  		t.Fatalf("Failed to remove container port using Apply patch: %v", err)
  1119  	}
  1120  
  1121  	deployment, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
  1122  	if err != nil {
  1123  		t.Fatalf("Failed to retrieve object: %v", err)
  1124  	}
  1125  
  1126  	if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 {
  1127  		t.Fatalf("Expected no container ports but got: %v, object: \n%#v", deployment.Spec.Template.Spec.Containers[0].Ports, deployment)
  1128  	}
  1129  }
  1130  
  1131  // TestApplyFailsWithVersionMismatch ensures that a version mismatch between the
  1132  // patch object and the live object will error
  1133  func TestApplyFailsWithVersionMismatch(t *testing.T) {
  1134  	client, closeFn := setup(t)
  1135  	defer closeFn()
  1136  
  1137  	obj := []byte(`{
  1138  		"apiVersion": "apps/v1",
  1139  		"kind": "Deployment",
  1140  		"metadata": {
  1141  			"name": "deployment",
  1142  			"labels": {"app": "nginx"}
  1143  		},
  1144  		"spec": {
  1145  			"replicas": 3,
  1146  			"selector": {
  1147  				"matchLabels": {
  1148  					 "app": "nginx"
  1149  				}
  1150  			},
  1151  			"template": {
  1152  				"metadata": {
  1153  					"labels": {
  1154  						"app": "nginx"
  1155  					}
  1156  				},
  1157  				"spec": {
  1158  					"containers": [{
  1159  						"name":  "nginx",
  1160  						"image": "nginx:latest"
  1161  					}]
  1162  				}
  1163  			}
  1164  		}
  1165  	}`)
  1166  
  1167  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1168  		AbsPath("/apis/apps/v1").
  1169  		Namespace("default").
  1170  		Resource("deployments").
  1171  		Name("deployment").
  1172  		Param("fieldManager", "apply_test").
  1173  		Body(obj).Do(context.TODO()).Get()
  1174  	if err != nil {
  1175  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1176  	}
  1177  
  1178  	obj = []byte(`{
  1179  		"apiVersion": "extensions/v1beta",
  1180  		"kind": "Deployment",
  1181  		"metadata": {
  1182  			"name": "deployment",
  1183  			"labels": {"app": "nginx"}
  1184  		},
  1185  		"spec": {
  1186  			"replicas": 100,
  1187  			"selector": {
  1188  				"matchLabels": {
  1189  					"app": "nginx"
  1190  				}
  1191  			},
  1192  			"template": {
  1193  				"metadata": {
  1194  					"labels": {
  1195  						"app": "nginx"
  1196  					}
  1197  				},
  1198  				"spec": {
  1199  					"containers": [{
  1200  						"name":  "nginx",
  1201  						"image": "nginx:latest"
  1202  					}]
  1203  				}
  1204  			}
  1205  		}
  1206  	}`)
  1207  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1208  		AbsPath("/apis/apps/v1").
  1209  		Namespace("default").
  1210  		Resource("deployments").
  1211  		Name("deployment").
  1212  		Param("fieldManager", "apply_test").
  1213  		Body([]byte(obj)).Do(context.TODO()).Get()
  1214  	if err == nil {
  1215  		t.Fatalf("Expecting to get version mismatch when applying object")
  1216  	}
  1217  	status, ok := err.(*apierrors.StatusError)
  1218  	if !ok {
  1219  		t.Fatalf("Expecting to get version mismatch as API error")
  1220  	}
  1221  	if status.Status().Code != http.StatusBadRequest {
  1222  		t.Fatalf("expected status code to be %d but was %d", http.StatusBadRequest, status.Status().Code)
  1223  	}
  1224  }
  1225  
  1226  // TestApplyConvertsManagedFieldsVersion checks that the apply
  1227  // converts the API group-version in the field manager
  1228  func TestApplyConvertsManagedFieldsVersion(t *testing.T) {
  1229  	client, closeFn := setup(t)
  1230  	defer closeFn()
  1231  
  1232  	obj := []byte(`{
  1233  		"apiVersion": "apps/v1",
  1234  		"kind": "Deployment",
  1235  		"metadata": {
  1236  			"name": "deployment",
  1237  			"labels": {"app": "nginx"},
  1238  			"managedFields": [
  1239  				{
  1240  					"manager": "sidecar_controller",
  1241  					"operation": "Apply",
  1242  					"apiVersion": "extensions/v1beta1",
  1243  					"fieldsV1": {
  1244  						"f:metadata": {
  1245  							"f:labels": {
  1246  								"f:sidecar_version": {}
  1247  							}
  1248  						},
  1249  						"f:spec": {
  1250  							"f:template": {
  1251  								"f: spec": {
  1252  									"f:containers": {
  1253  										"k:{\"name\":\"sidecar\"}": {
  1254  											".": {},
  1255  											"f:image": {}
  1256  										}
  1257  									}
  1258  								}
  1259  							}
  1260  						}
  1261  					}
  1262  				}
  1263  			]
  1264  		},
  1265  		"spec": {
  1266  			"selector": {
  1267  				"matchLabels": {
  1268  					 "app": "nginx"
  1269  				}
  1270  			},
  1271  			"template": {
  1272  				"metadata": {
  1273  					"labels": {
  1274  						"app": "nginx"
  1275  					}
  1276  				},
  1277  				"spec": {
  1278  					"containers": [{
  1279  						"name":  "nginx",
  1280  						"image": "nginx:latest"
  1281  					}]
  1282  				}
  1283  			}
  1284  		}
  1285  	}`)
  1286  
  1287  	_, err := client.CoreV1().RESTClient().Post().
  1288  		AbsPath("/apis/apps/v1").
  1289  		Namespace("default").
  1290  		Resource("deployments").
  1291  		Body(obj).Do(context.TODO()).Get()
  1292  	if err != nil {
  1293  		t.Fatalf("Failed to create object: %v", err)
  1294  	}
  1295  
  1296  	obj = []byte(`{
  1297  		"apiVersion": "apps/v1",
  1298  		"kind": "Deployment",
  1299  		"metadata": {
  1300  			"name": "deployment",
  1301  			"labels": {"sidecar_version": "release"}
  1302  		},
  1303  		"spec": {
  1304  			"template": {
  1305  				"spec": {
  1306  					"containers": [{
  1307  						"name":  "sidecar",
  1308  						"image": "sidecar:latest"
  1309  					}]
  1310  				}
  1311  			}
  1312  		}
  1313  	}`)
  1314  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1315  		AbsPath("/apis/apps/v1").
  1316  		Namespace("default").
  1317  		Resource("deployments").
  1318  		Name("deployment").
  1319  		Param("fieldManager", "sidecar_controller").
  1320  		Body([]byte(obj)).Do(context.TODO()).Get()
  1321  	if err != nil {
  1322  		t.Fatalf("Failed to apply object: %v", err)
  1323  	}
  1324  
  1325  	object, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
  1326  	if err != nil {
  1327  		t.Fatalf("Failed to retrieve object: %v", err)
  1328  	}
  1329  
  1330  	accessor, err := meta.Accessor(object)
  1331  	if err != nil {
  1332  		t.Fatalf("Failed to get meta accessor: %v", err)
  1333  	}
  1334  
  1335  	managed := accessor.GetManagedFields()
  1336  	if len(managed) != 2 {
  1337  		t.Fatalf("Expected 2 field managers, but got managed fields: %v", managed)
  1338  	}
  1339  
  1340  	var actual *metav1.ManagedFieldsEntry
  1341  	for i := range managed {
  1342  		entry := &managed[i]
  1343  		if entry.Manager == "sidecar_controller" && entry.APIVersion == "apps/v1" {
  1344  			actual = entry
  1345  		}
  1346  	}
  1347  
  1348  	if actual == nil {
  1349  		t.Fatalf("Expected managed fields to contain entry with manager '%v' with converted api version '%v', but got managed fields:\n%v", "sidecar_controller", "apps/v1", managed)
  1350  	}
  1351  
  1352  	expected := &metav1.ManagedFieldsEntry{
  1353  		Manager:    "sidecar_controller",
  1354  		Operation:  metav1.ManagedFieldsOperationApply,
  1355  		APIVersion: "apps/v1",
  1356  		Time:       actual.Time,
  1357  		FieldsType: "FieldsV1",
  1358  		FieldsV1: &metav1.FieldsV1{
  1359  			Raw: []byte(`{"f:metadata":{"f:labels":{"f:sidecar_version":{}}},"f:spec":{"f:template":{"f:spec":{"f:containers":{"k:{\"name\":\"sidecar\"}":{".":{},"f:image":{},"f:name":{}}}}}}}`),
  1360  		},
  1361  	}
  1362  
  1363  	if !reflect.DeepEqual(actual, expected) {
  1364  		t.Fatalf("expected:\n%v\nbut got:\n%v", expected, actual)
  1365  	}
  1366  }
  1367  
  1368  // TestClearManagedFieldsWithMergePatch verifies it's possible to clear the managedFields
  1369  func TestClearManagedFieldsWithMergePatch(t *testing.T) {
  1370  	client, closeFn := setup(t)
  1371  	defer closeFn()
  1372  
  1373  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1374  		Namespace("default").
  1375  		Resource("configmaps").
  1376  		Name("test-cm").
  1377  		Param("fieldManager", "apply_test").
  1378  		Body([]byte(`{
  1379  			"apiVersion": "v1",
  1380  			"kind": "ConfigMap",
  1381  			"metadata": {
  1382  				"name": "test-cm",
  1383  				"namespace": "default",
  1384  				"labels": {
  1385  					"test-label": "test"
  1386  				}
  1387  			},
  1388  			"data": {
  1389  				"key": "value"
  1390  			}
  1391  		}`)).
  1392  		Do(context.TODO()).
  1393  		Get()
  1394  	if err != nil {
  1395  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1396  	}
  1397  
  1398  	_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  1399  		Namespace("default").
  1400  		Resource("configmaps").
  1401  		Name("test-cm").
  1402  		Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
  1403  	if err != nil {
  1404  		t.Fatalf("Failed to patch object: %v", err)
  1405  	}
  1406  
  1407  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1408  	if err != nil {
  1409  		t.Fatalf("Failed to retrieve object: %v", err)
  1410  	}
  1411  
  1412  	accessor, err := meta.Accessor(object)
  1413  	if err != nil {
  1414  		t.Fatalf("Failed to get meta accessor: %v", err)
  1415  	}
  1416  
  1417  	if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1418  		t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1419  	}
  1420  }
  1421  
  1422  // TestClearManagedFieldsWithStrategicMergePatch verifies it's possible to clear the managedFields
  1423  func TestClearManagedFieldsWithStrategicMergePatch(t *testing.T) {
  1424  	client, closeFn := setup(t)
  1425  	defer closeFn()
  1426  
  1427  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1428  		Namespace("default").
  1429  		Resource("configmaps").
  1430  		Name("test-cm").
  1431  		Param("fieldManager", "apply_test").
  1432  		Body([]byte(`{
  1433  			"apiVersion": "v1",
  1434  			"kind": "ConfigMap",
  1435  			"metadata": {
  1436  				"name": "test-cm",
  1437  				"namespace": "default",
  1438  				"labels": {
  1439  					"test-label": "test"
  1440  				}
  1441  			},
  1442  			"data": {
  1443  				"key": "value"
  1444  			}
  1445  		}`)).
  1446  		Do(context.TODO()).
  1447  		Get()
  1448  	if err != nil {
  1449  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1450  	}
  1451  
  1452  	_, err = client.CoreV1().RESTClient().Patch(types.StrategicMergePatchType).
  1453  		Namespace("default").
  1454  		Resource("configmaps").
  1455  		Name("test-cm").
  1456  		Body([]byte(`{"metadata":{"managedFields": [{}]}}`)).Do(context.TODO()).Get()
  1457  	if err != nil {
  1458  		t.Fatalf("Failed to patch object: %v", err)
  1459  	}
  1460  
  1461  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1462  	if err != nil {
  1463  		t.Fatalf("Failed to retrieve object: %v", err)
  1464  	}
  1465  
  1466  	accessor, err := meta.Accessor(object)
  1467  	if err != nil {
  1468  		t.Fatalf("Failed to get meta accessor: %v", err)
  1469  	}
  1470  
  1471  	if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1472  		t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1473  	}
  1474  
  1475  	if labels := accessor.GetLabels(); len(labels) < 1 {
  1476  		t.Fatalf("Expected other fields to stay untouched, got: %v", object)
  1477  	}
  1478  }
  1479  
  1480  // TestClearManagedFieldsWithJSONPatch verifies it's possible to clear the managedFields
  1481  func TestClearManagedFieldsWithJSONPatch(t *testing.T) {
  1482  	client, closeFn := setup(t)
  1483  	defer closeFn()
  1484  
  1485  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1486  		Namespace("default").
  1487  		Resource("configmaps").
  1488  		Name("test-cm").
  1489  		Param("fieldManager", "apply_test").
  1490  		Body([]byte(`{
  1491  			"apiVersion": "v1",
  1492  			"kind": "ConfigMap",
  1493  			"metadata": {
  1494  				"name": "test-cm",
  1495  				"namespace": "default",
  1496  				"labels": {
  1497  					"test-label": "test"
  1498  				}
  1499  			},
  1500  			"data": {
  1501  				"key": "value"
  1502  			}
  1503  		}`)).
  1504  		Do(context.TODO()).
  1505  		Get()
  1506  	if err != nil {
  1507  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1508  	}
  1509  
  1510  	_, err = client.CoreV1().RESTClient().Patch(types.JSONPatchType).
  1511  		Namespace("default").
  1512  		Resource("configmaps").
  1513  		Name("test-cm").
  1514  		Body([]byte(`[{"op": "replace", "path": "/metadata/managedFields", "value": [{}]}]`)).Do(context.TODO()).Get()
  1515  	if err != nil {
  1516  		t.Fatalf("Failed to patch object: %v", err)
  1517  	}
  1518  
  1519  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1520  	if err != nil {
  1521  		t.Fatalf("Failed to retrieve object: %v", err)
  1522  	}
  1523  
  1524  	accessor, err := meta.Accessor(object)
  1525  	if err != nil {
  1526  		t.Fatalf("Failed to get meta accessor: %v", err)
  1527  	}
  1528  
  1529  	if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1530  		t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1531  	}
  1532  }
  1533  
  1534  // TestClearManagedFieldsWithUpdate verifies it's possible to clear the managedFields
  1535  func TestClearManagedFieldsWithUpdate(t *testing.T) {
  1536  	client, closeFn := setup(t)
  1537  	defer closeFn()
  1538  
  1539  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1540  		Namespace("default").
  1541  		Resource("configmaps").
  1542  		Name("test-cm").
  1543  		Param("fieldManager", "apply_test").
  1544  		Body([]byte(`{
  1545  			"apiVersion": "v1",
  1546  			"kind": "ConfigMap",
  1547  			"metadata": {
  1548  				"name": "test-cm",
  1549  				"namespace": "default",
  1550  				"labels": {
  1551  					"test-label": "test"
  1552  				}
  1553  			},
  1554  			"data": {
  1555  				"key": "value"
  1556  			}
  1557  		}`)).
  1558  		Do(context.TODO()).
  1559  		Get()
  1560  	if err != nil {
  1561  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1562  	}
  1563  
  1564  	_, err = client.CoreV1().RESTClient().Put().
  1565  		Namespace("default").
  1566  		Resource("configmaps").
  1567  		Name("test-cm").
  1568  		Body([]byte(`{
  1569  			"apiVersion": "v1",
  1570  			"kind": "ConfigMap",
  1571  			"metadata": {
  1572  				"name": "test-cm",
  1573  				"namespace": "default",
  1574  				"managedFields": [{}],
  1575  				"labels": {
  1576  					"test-label": "test"
  1577  				}
  1578  			},
  1579  			"data": {
  1580  				"key": "value"
  1581  			}
  1582  		}`)).Do(context.TODO()).Get()
  1583  	if err != nil {
  1584  		t.Fatalf("Failed to patch object: %v", err)
  1585  	}
  1586  
  1587  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1588  	if err != nil {
  1589  		t.Fatalf("Failed to retrieve object: %v", err)
  1590  	}
  1591  
  1592  	accessor, err := meta.Accessor(object)
  1593  	if err != nil {
  1594  		t.Fatalf("Failed to get meta accessor: %v", err)
  1595  	}
  1596  
  1597  	if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1598  		t.Fatalf("Failed to clear managedFields, got: %v", managedFields)
  1599  	}
  1600  
  1601  	if labels := accessor.GetLabels(); len(labels) < 1 {
  1602  		t.Fatalf("Expected other fields to stay untouched, got: %v", object)
  1603  	}
  1604  }
  1605  
  1606  // TestErrorsDontFail
  1607  func TestErrorsDontFail(t *testing.T) {
  1608  	client, closeFn := setup(t)
  1609  	defer closeFn()
  1610  
  1611  	// Tries to create with a managed fields that has an empty `fieldsType`.
  1612  	_, err := client.CoreV1().RESTClient().Post().
  1613  		Namespace("default").
  1614  		Resource("configmaps").
  1615  		Param("fieldManager", "apply_test").
  1616  		Body([]byte(`{
  1617  			"apiVersion": "v1",
  1618  			"kind": "ConfigMap",
  1619  			"metadata": {
  1620  				"name": "test-cm",
  1621  				"namespace": "default",
  1622  				"managedFields": [{
  1623  					"manager": "apply_test",
  1624  					"operation": "Apply",
  1625  					"apiVersion": "v1",
  1626  					"time": "2019-07-08T09:31:18Z",
  1627  					"fieldsType": "",
  1628  					"fieldsV1": {}
  1629  				}],
  1630  				"labels": {
  1631  					"test-label": "test"
  1632  				}
  1633  			},
  1634  			"data": {
  1635  				"key": "value"
  1636  			}
  1637  		}`)).
  1638  		Do(context.TODO()).
  1639  		Get()
  1640  	if err != nil {
  1641  		t.Fatalf("Failed to create object with empty fieldsType: %v", err)
  1642  	}
  1643  }
  1644  
  1645  func TestErrorsDontFailUpdate(t *testing.T) {
  1646  	client, closeFn := setup(t)
  1647  	defer closeFn()
  1648  
  1649  	_, err := client.CoreV1().RESTClient().Post().
  1650  		Namespace("default").
  1651  		Resource("configmaps").
  1652  		Param("fieldManager", "apply_test").
  1653  		Body([]byte(`{
  1654  			"apiVersion": "v1",
  1655  			"kind": "ConfigMap",
  1656  			"metadata": {
  1657  				"name": "test-cm",
  1658  				"namespace": "default",
  1659  				"labels": {
  1660  					"test-label": "test"
  1661  				}
  1662  			},
  1663  			"data": {
  1664  				"key": "value"
  1665  			}
  1666  		}`)).
  1667  		Do(context.TODO()).
  1668  		Get()
  1669  	if err != nil {
  1670  		t.Fatalf("Failed to create object: %v", err)
  1671  	}
  1672  
  1673  	_, err = client.CoreV1().RESTClient().Put().
  1674  		Namespace("default").
  1675  		Resource("configmaps").
  1676  		Name("test-cm").
  1677  		Param("fieldManager", "apply_test").
  1678  		Body([]byte(`{
  1679  			"apiVersion": "v1",
  1680  			"kind": "ConfigMap",
  1681  			"metadata": {
  1682  				"name": "test-cm",
  1683  				"namespace": "default",
  1684  				"managedFields": [{
  1685  					"manager": "apply_test",
  1686  					"operation": "Apply",
  1687  					"apiVersion": "v1",
  1688  					"time": "2019-07-08T09:31:18Z",
  1689  					"fieldsType": "",
  1690  					"fieldsV1": {}
  1691  				}],
  1692  				"labels": {
  1693  					"test-label": "test"
  1694  				}
  1695  			},
  1696  			"data": {
  1697  				"key": "value"
  1698  			}
  1699  		}`)).
  1700  		Do(context.TODO()).
  1701  		Get()
  1702  	if err != nil {
  1703  		t.Fatalf("Failed to update object with empty fieldsType: %v", err)
  1704  	}
  1705  }
  1706  
  1707  func TestErrorsDontFailPatch(t *testing.T) {
  1708  	client, closeFn := setup(t)
  1709  	defer closeFn()
  1710  
  1711  	_, err := client.CoreV1().RESTClient().Post().
  1712  		Namespace("default").
  1713  		Resource("configmaps").
  1714  		Param("fieldManager", "apply_test").
  1715  		Body([]byte(`{
  1716  			"apiVersion": "v1",
  1717  			"kind": "ConfigMap",
  1718  			"metadata": {
  1719  				"name": "test-cm",
  1720  				"namespace": "default",
  1721  				"labels": {
  1722  					"test-label": "test"
  1723  				}
  1724  			},
  1725  			"data": {
  1726  				"key": "value"
  1727  			}
  1728  		}`)).
  1729  		Do(context.TODO()).
  1730  		Get()
  1731  	if err != nil {
  1732  		t.Fatalf("Failed to create object: %v", err)
  1733  	}
  1734  
  1735  	_, err = client.CoreV1().RESTClient().Patch(types.JSONPatchType).
  1736  		Namespace("default").
  1737  		Resource("configmaps").
  1738  		Name("test-cm").
  1739  		Param("fieldManager", "apply_test").
  1740  		Body([]byte(`[{"op": "replace", "path": "/metadata/managedFields", "value": [{
  1741  			"manager": "apply_test",
  1742  			"operation": "Apply",
  1743  			"apiVersion": "v1",
  1744  			"time": "2019-07-08T09:31:18Z",
  1745  			"fieldsType": "",
  1746  			"fieldsV1": {}
  1747  		}]}]`)).
  1748  		Do(context.TODO()).
  1749  		Get()
  1750  	if err != nil {
  1751  		t.Fatalf("Failed to patch object with empty FieldsType: %v", err)
  1752  	}
  1753  }
  1754  
  1755  func TestApplyDoesNotChangeManagedFieldsViaSubresources(t *testing.T) {
  1756  	client, closeFn := setup(t)
  1757  	defer closeFn()
  1758  
  1759  	podBytes := []byte(`{
  1760  		"apiVersion": "v1",
  1761  		"kind": "Pod",
  1762  		"metadata": {
  1763  			"name": "just-a-pod"
  1764  		},
  1765  		"spec": {
  1766  			"containers": [{
  1767  				"name":  "test-container-a",
  1768  				"image": "test-image-one"
  1769  			}]
  1770  		}
  1771  	}`)
  1772  
  1773  	liveObj, err := client.CoreV1().RESTClient().
  1774  		Patch(types.ApplyPatchType).
  1775  		Namespace("default").
  1776  		Param("fieldManager", "apply_test").
  1777  		Resource("pods").
  1778  		Name("just-a-pod").
  1779  		Body(podBytes).
  1780  		Do(context.TODO()).
  1781  		Get()
  1782  	if err != nil {
  1783  		t.Fatalf("Failed to create object: %v", err)
  1784  	}
  1785  
  1786  	updateBytes := []byte(`{
  1787  		"metadata": {
  1788  			"managedFields": [{
  1789  				"manager":"testing",
  1790  				"operation":"Update",
  1791  				"apiVersion":"v1",
  1792  				"fieldsType":"FieldsV1",
  1793  				"fieldsV1":{
  1794  					"f:spec":{
  1795  						"f:containers":{
  1796  							"k:{\"name\":\"testing\"}":{
  1797  								".":{},
  1798  								"f:image":{},
  1799  								"f:name":{}
  1800  							}
  1801  						}
  1802  					}
  1803  				}
  1804  			}]
  1805  		},
  1806  		"status": {
  1807  			"conditions": [{"type": "MyStatus", "status":"true"}]
  1808  		}
  1809  	}`)
  1810  
  1811  	updateActor := "update_managedfields_test"
  1812  	newObj, err := client.CoreV1().RESTClient().
  1813  		Patch(types.MergePatchType).
  1814  		Namespace("default").
  1815  		Param("fieldManager", updateActor).
  1816  		Name("just-a-pod").
  1817  		Resource("pods").
  1818  		SubResource("status").
  1819  		Body(updateBytes).
  1820  		Do(context.TODO()).
  1821  		Get()
  1822  
  1823  	if err != nil {
  1824  		t.Fatalf("Error updating subresource: %v ", err)
  1825  	}
  1826  
  1827  	liveAccessor, err := meta.Accessor(liveObj)
  1828  	if err != nil {
  1829  		t.Fatalf("Failed to get meta accessor for live object: %v", err)
  1830  	}
  1831  	newAccessor, err := meta.Accessor(newObj)
  1832  	if err != nil {
  1833  		t.Fatalf("Failed to get meta accessor for new object: %v", err)
  1834  	}
  1835  
  1836  	liveManagedFields := liveAccessor.GetManagedFields()
  1837  	if len(liveManagedFields) != 1 {
  1838  		t.Fatalf("Expected managedFields in the live object to have exactly one entry, got %d: %v", len(liveManagedFields), liveManagedFields)
  1839  	}
  1840  
  1841  	newManagedFields := newAccessor.GetManagedFields()
  1842  	if len(newManagedFields) != 2 {
  1843  		t.Fatalf("Expected managedFields in the new object to have exactly two entries, got %d: %v", len(newManagedFields), newManagedFields)
  1844  	}
  1845  
  1846  	if !reflect.DeepEqual(liveManagedFields[0], newManagedFields[0]) {
  1847  		t.Fatalf("managedFields updated via subresource:\n\nlive managedFields: %v\nnew managedFields: %v\n\n", liveManagedFields, newManagedFields)
  1848  	}
  1849  
  1850  	if newManagedFields[1].Manager != updateActor {
  1851  		t.Fatalf(`Expected managerFields to have an entry with manager set to %q`, updateActor)
  1852  	}
  1853  }
  1854  
  1855  // TestClearManagedFieldsWithUpdateEmptyList verifies it's possible to clear the managedFields by sending an empty list.
  1856  func TestClearManagedFieldsWithUpdateEmptyList(t *testing.T) {
  1857  	client, closeFn := setup(t)
  1858  	defer closeFn()
  1859  
  1860  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1861  		Namespace("default").
  1862  		Resource("configmaps").
  1863  		Name("test-cm").
  1864  		Param("fieldManager", "apply_test").
  1865  		Body([]byte(`{
  1866  			"apiVersion": "v1",
  1867  			"kind": "ConfigMap",
  1868  			"metadata": {
  1869  				"name": "test-cm",
  1870  				"namespace": "default",
  1871  				"labels": {
  1872  					"test-label": "test"
  1873  				}
  1874  			},
  1875  			"data": {
  1876  				"key": "value"
  1877  			}
  1878  		}`)).
  1879  		Do(context.TODO()).
  1880  		Get()
  1881  	if err != nil {
  1882  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1883  	}
  1884  
  1885  	_, err = client.CoreV1().RESTClient().Put().
  1886  		Namespace("default").
  1887  		Resource("configmaps").
  1888  		Name("test-cm").
  1889  		Body([]byte(`{
  1890  			"apiVersion": "v1",
  1891  			"kind": "ConfigMap",
  1892  			"metadata": {
  1893  				"name": "test-cm",
  1894  				"namespace": "default",
  1895  				"managedFields": [],
  1896  				"labels": {
  1897  					"test-label": "test"
  1898  				}
  1899  			},
  1900  			"data": {
  1901  				"key": "value"
  1902  			}
  1903  		}`)).Do(context.TODO()).Get()
  1904  	if err != nil {
  1905  		t.Fatalf("Failed to patch object: %v", err)
  1906  	}
  1907  
  1908  	_, err = client.CoreV1().RESTClient().Patch(types.MergePatchType).
  1909  		Namespace("default").
  1910  		Resource("configmaps").
  1911  		Name("test-cm").
  1912  		Body([]byte(`{"metadata":{"labels": { "test-label": "v1" }}}`)).Do(context.TODO()).Get()
  1913  
  1914  	if err != nil {
  1915  		t.Fatalf("Failed to patch object: %v", err)
  1916  	}
  1917  
  1918  	object, err := client.CoreV1().RESTClient().Get().Namespace("default").Resource("configmaps").Name("test-cm").Do(context.TODO()).Get()
  1919  	if err != nil {
  1920  		t.Fatalf("Failed to retrieve object: %v", err)
  1921  	}
  1922  
  1923  	accessor, err := meta.Accessor(object)
  1924  	if err != nil {
  1925  		t.Fatalf("Failed to get meta accessor: %v", err)
  1926  	}
  1927  
  1928  	if managedFields := accessor.GetManagedFields(); len(managedFields) != 0 {
  1929  		t.Fatalf("Failed to stop tracking managedFields, got: %v", managedFields)
  1930  	}
  1931  
  1932  	if labels := accessor.GetLabels(); len(labels) < 1 {
  1933  		t.Fatalf("Expected other fields to stay untouched, got: %v", object)
  1934  	}
  1935  }
  1936  
  1937  // TestApplyUnsetExclusivelyOwnedFields verifies that when owned fields are omitted from an applied
  1938  // configuration, and no other managers own the field, it is removed.
  1939  func TestApplyUnsetExclusivelyOwnedFields(t *testing.T) {
  1940  	client, closeFn := setup(t)
  1941  	defer closeFn()
  1942  
  1943  	// spec.replicas is a optional, defaulted field
  1944  	// spec.template.spec.hostname is an optional, non-defaulted field
  1945  	apply := []byte(`{
  1946  		"apiVersion": "apps/v1",
  1947  		"kind": "Deployment",
  1948  		"metadata": {
  1949  			"name": "deployment-exclusive-unset",
  1950  			"labels": {"app": "nginx"}
  1951  		},
  1952  		"spec": {
  1953  			"replicas": 3,
  1954  			"selector": {
  1955  				"matchLabels": {
  1956  					"app": "nginx"
  1957  				}
  1958  			},
  1959  			"template": {
  1960  				"metadata": {
  1961  					"labels": {
  1962  						"app": "nginx"
  1963  					}
  1964  				},
  1965  				"spec": {
  1966  					"hostname": "test-hostname",
  1967  					"containers": [{
  1968  						"name":  "nginx",
  1969  						"image": "nginx:latest"
  1970  					}]
  1971  				}
  1972  			}
  1973  		}
  1974  	}`)
  1975  
  1976  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  1977  		AbsPath("/apis/apps/v1").
  1978  		Namespace("default").
  1979  		Resource("deployments").
  1980  		Name("deployment-exclusive-unset").
  1981  		Param("fieldManager", "apply_test").
  1982  		Body(apply).
  1983  		Do(context.TODO()).
  1984  		Get()
  1985  	if err != nil {
  1986  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  1987  	}
  1988  
  1989  	// unset spec.replicas and spec.template.spec.hostname
  1990  	apply = []byte(`{
  1991  		"apiVersion": "apps/v1",
  1992  		"kind": "Deployment",
  1993  		"metadata": {
  1994  			"name": "deployment-exclusive-unset",
  1995  			"labels": {"app": "nginx"}
  1996  		},
  1997  		"spec": {
  1998  			"selector": {
  1999  				"matchLabels": {
  2000  					"app": "nginx"
  2001  				}
  2002  			},
  2003  			"template": {
  2004  				"metadata": {
  2005  					"labels": {
  2006  						"app": "nginx"
  2007  					}
  2008  				},
  2009  				"spec": {
  2010  					"containers": [{
  2011  						"name":  "nginx",
  2012  						"image": "nginx:latest"
  2013  					}]
  2014  				}
  2015  			}
  2016  		}
  2017  	}`)
  2018  
  2019  	patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2020  		AbsPath("/apis/apps/v1").
  2021  		Namespace("default").
  2022  		Resource("deployments").
  2023  		Name("deployment-exclusive-unset").
  2024  		Param("fieldManager", "apply_test").
  2025  		Body(apply).
  2026  		Do(context.TODO()).
  2027  		Get()
  2028  	if err != nil {
  2029  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2030  	}
  2031  
  2032  	deployment, ok := patched.(*appsv1.Deployment)
  2033  	if !ok {
  2034  		t.Fatalf("Failed to convert response object to Deployment")
  2035  	}
  2036  	if *deployment.Spec.Replicas != 1 {
  2037  		t.Errorf("Expected deployment.spec.replicas to be 1 (default value), but got %d", deployment.Spec.Replicas)
  2038  	}
  2039  	if len(deployment.Spec.Template.Spec.Hostname) != 0 {
  2040  		t.Errorf("Expected deployment.spec.template.spec.hostname to be unset, but got %s", deployment.Spec.Template.Spec.Hostname)
  2041  	}
  2042  }
  2043  
  2044  // TestApplyUnsetSharedFields verifies that when owned fields are omitted from an applied
  2045  // configuration, but other managers also own the field, is it not removed.
  2046  func TestApplyUnsetSharedFields(t *testing.T) {
  2047  	client, closeFn := setup(t)
  2048  	defer closeFn()
  2049  
  2050  	// spec.replicas is a optional, defaulted field
  2051  	// spec.template.spec.hostname is an optional, non-defaulted field
  2052  	apply := []byte(`{
  2053  		"apiVersion": "apps/v1",
  2054  		"kind": "Deployment",
  2055  		"metadata": {
  2056  			"name": "deployment-shared-unset",
  2057  			"labels": {"app": "nginx"}
  2058  		},
  2059  		"spec": {
  2060  			"replicas": 3,
  2061  			"selector": {
  2062  				"matchLabels": {
  2063  					"app": "nginx"
  2064  				}
  2065  			},
  2066  			"template": {
  2067  				"metadata": {
  2068  					"labels": {
  2069  						"app": "nginx"
  2070  					}
  2071  				},
  2072  				"spec": {
  2073  					"hostname": "test-hostname",
  2074  					"containers": [{
  2075  						"name":  "nginx",
  2076  						"image": "nginx:latest"
  2077  					}]
  2078  				}
  2079  			}
  2080  		}
  2081  	}`)
  2082  
  2083  	for _, fieldManager := range []string{"shared_owner_1", "shared_owner_2"} {
  2084  		_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2085  			AbsPath("/apis/apps/v1").
  2086  			Namespace("default").
  2087  			Resource("deployments").
  2088  			Name("deployment-shared-unset").
  2089  			Param("fieldManager", fieldManager).
  2090  			Body(apply).
  2091  			Do(context.TODO()).
  2092  			Get()
  2093  		if err != nil {
  2094  			t.Fatalf("Failed to create object using Apply patch: %v", err)
  2095  		}
  2096  	}
  2097  
  2098  	// unset spec.replicas and spec.template.spec.hostname
  2099  	apply = []byte(`{
  2100  		"apiVersion": "apps/v1",
  2101  		"kind": "Deployment",
  2102  		"metadata": {
  2103  			"name": "deployment-shared-unset",
  2104  			"labels": {"app": "nginx"}
  2105  		},
  2106  		"spec": {
  2107  			"selector": {
  2108  				"matchLabels": {
  2109  					"app": "nginx"
  2110  				}
  2111  			},
  2112  			"template": {
  2113  				"metadata": {
  2114  					"labels": {
  2115  						"app": "nginx"
  2116  					}
  2117  				},
  2118  				"spec": {
  2119  					"containers": [{
  2120  						"name":  "nginx",
  2121  						"image": "nginx:latest"
  2122  					}]
  2123  				}
  2124  			}
  2125  		}
  2126  	}`)
  2127  
  2128  	patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2129  		AbsPath("/apis/apps/v1").
  2130  		Namespace("default").
  2131  		Resource("deployments").
  2132  		Name("deployment-shared-unset").
  2133  		Param("fieldManager", "shared_owner_1").
  2134  		Body(apply).
  2135  		Do(context.TODO()).
  2136  		Get()
  2137  	if err != nil {
  2138  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2139  	}
  2140  
  2141  	deployment, ok := patched.(*appsv1.Deployment)
  2142  	if !ok {
  2143  		t.Fatalf("Failed to convert response object to Deployment")
  2144  	}
  2145  	if *deployment.Spec.Replicas != 3 {
  2146  		t.Errorf("Expected deployment.spec.replicas to be 3, but got %d", deployment.Spec.Replicas)
  2147  	}
  2148  	if deployment.Spec.Template.Spec.Hostname != "test-hostname" {
  2149  		t.Errorf("Expected deployment.spec.template.spec.hostname to be \"test-hostname\", but got %s", deployment.Spec.Template.Spec.Hostname)
  2150  	}
  2151  }
  2152  
  2153  // TestApplyCanTransferFieldOwnershipToController verifies that when an applier creates an
  2154  // object, a controller takes ownership of a field, and the applier
  2155  // then omits the field from its applied configuration, that the field value persists.
  2156  func TestApplyCanTransferFieldOwnershipToController(t *testing.T) {
  2157  	client, closeFn := setup(t)
  2158  	defer closeFn()
  2159  
  2160  	// Applier creates a deployment with replicas set to 3
  2161  	apply := []byte(`{
  2162  		"apiVersion": "apps/v1",
  2163  		"kind": "Deployment",
  2164  		"metadata": {
  2165  			"name": "deployment-shared-map-item-removal",
  2166  			"labels": {"app": "nginx"}
  2167  		},
  2168  		"spec": {
  2169  			"replicas": 3,
  2170  			"selector": {
  2171  				"matchLabels": {
  2172  					"app": "nginx"
  2173  				}
  2174  			},
  2175  			"template": {
  2176  				"metadata": {
  2177  					"labels": {
  2178  						"app": "nginx"
  2179  					}
  2180  				},
  2181  				"spec": {
  2182  					"containers": [{
  2183  						"name":  "nginx",
  2184  						"image": "nginx:latest",
  2185  					}]
  2186  				}
  2187  			}
  2188  		}
  2189  	}`)
  2190  
  2191  	appliedObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2192  		AbsPath("/apis/apps/v1").
  2193  		Namespace("default").
  2194  		Resource("deployments").
  2195  		Name("deployment-shared-map-item-removal").
  2196  		Param("fieldManager", "test_applier").
  2197  		Body(apply).
  2198  		Do(context.TODO()).
  2199  		Get()
  2200  	if err != nil {
  2201  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2202  	}
  2203  
  2204  	// a controller takes over the replicas field
  2205  	applied, ok := appliedObj.(*appsv1.Deployment)
  2206  	if !ok {
  2207  		t.Fatalf("Failed to convert response object to Deployment")
  2208  	}
  2209  	replicas := int32(4)
  2210  	applied.Spec.Replicas = &replicas
  2211  	_, err = client.AppsV1().Deployments("default").
  2212  		Update(context.TODO(), applied, metav1.UpdateOptions{FieldManager: "test_updater"})
  2213  	if err != nil {
  2214  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2215  	}
  2216  
  2217  	// applier omits replicas
  2218  	apply = []byte(`{
  2219  		"apiVersion": "apps/v1",
  2220  		"kind": "Deployment",
  2221  		"metadata": {
  2222  			"name": "deployment-shared-map-item-removal",
  2223  			"labels": {"app": "nginx"}
  2224  		},
  2225  		"spec": {
  2226  			"selector": {
  2227  				"matchLabels": {
  2228  					"app": "nginx"
  2229  				}
  2230  			},
  2231  			"template": {
  2232  				"metadata": {
  2233  					"labels": {
  2234  						"app": "nginx"
  2235  					}
  2236  				},
  2237  				"spec": {
  2238  					"containers": [{
  2239  						"name":  "nginx",
  2240  						"image": "nginx:latest",
  2241  					}]
  2242  				}
  2243  			}
  2244  		}
  2245  	}`)
  2246  
  2247  	patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2248  		AbsPath("/apis/apps/v1").
  2249  		Namespace("default").
  2250  		Resource("deployments").
  2251  		Name("deployment-shared-map-item-removal").
  2252  		Param("fieldManager", "test_applier").
  2253  		Body(apply).
  2254  		Do(context.TODO()).
  2255  		Get()
  2256  	if err != nil {
  2257  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2258  	}
  2259  
  2260  	// ensure the container is deleted even though a controller updated a field of the container
  2261  	deployment, ok := patched.(*appsv1.Deployment)
  2262  	if !ok {
  2263  		t.Fatalf("Failed to convert response object to Deployment")
  2264  	}
  2265  	if *deployment.Spec.Replicas != 4 {
  2266  		t.Errorf("Expected deployment.spec.replicas to be 4, but got %d", deployment.Spec.Replicas)
  2267  	}
  2268  }
  2269  
  2270  // TestApplyCanRemoveMapItemsContributedToByControllers verifies that when an applier creates an
  2271  // object, a controller modifies the contents of the map item via update, and the applier
  2272  // then omits the item from its applied configuration, that the item is removed.
  2273  func TestApplyCanRemoveMapItemsContributedToByControllers(t *testing.T) {
  2274  	client, closeFn := setup(t)
  2275  	defer closeFn()
  2276  
  2277  	// Applier creates a deployment with a name=nginx container
  2278  	apply := []byte(`{
  2279  		"apiVersion": "apps/v1",
  2280  		"kind": "Deployment",
  2281  		"metadata": {
  2282  			"name": "deployment-shared-map-item-removal",
  2283  			"labels": {"app": "nginx"}
  2284  		},
  2285  		"spec": {
  2286  			"selector": {
  2287  				"matchLabels": {
  2288  					"app": "nginx"
  2289  				}
  2290  			},
  2291  			"template": {
  2292  				"metadata": {
  2293  					"labels": {
  2294  						"app": "nginx"
  2295  					}
  2296  				},
  2297  				"spec": {
  2298  					"containers": [{
  2299  						"name":  "nginx",
  2300  						"image": "nginx:latest",
  2301  					}]
  2302  				}
  2303  			}
  2304  		}
  2305  	}`)
  2306  
  2307  	appliedObj, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2308  		AbsPath("/apis/apps/v1").
  2309  		Namespace("default").
  2310  		Resource("deployments").
  2311  		Name("deployment-shared-map-item-removal").
  2312  		Param("fieldManager", "test_applier").
  2313  		Body(apply).
  2314  		Do(context.TODO()).
  2315  		Get()
  2316  	if err != nil {
  2317  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2318  	}
  2319  
  2320  	// a controller sets container.workingDir of the name=nginx container via an update
  2321  	applied, ok := appliedObj.(*appsv1.Deployment)
  2322  	if !ok {
  2323  		t.Fatalf("Failed to convert response object to Deployment")
  2324  	}
  2325  	applied.Spec.Template.Spec.Containers[0].WorkingDir = "/home/replacement"
  2326  	_, err = client.AppsV1().Deployments("default").
  2327  		Update(context.TODO(), applied, metav1.UpdateOptions{FieldManager: "test_updater"})
  2328  	if err != nil {
  2329  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2330  	}
  2331  
  2332  	// applier removes name=nginx the container
  2333  	apply = []byte(`{
  2334  		"apiVersion": "apps/v1",
  2335  		"kind": "Deployment",
  2336  		"metadata": {
  2337  			"name": "deployment-shared-map-item-removal",
  2338  			"labels": {"app": "nginx"}
  2339  		},
  2340  		"spec": {
  2341  			"replicas": 3,
  2342  			"selector": {
  2343  				"matchLabels": {
  2344  					"app": "nginx"
  2345  				}
  2346  			},
  2347  			"template": {
  2348  				"metadata": {
  2349  					"labels": {
  2350  						"app": "nginx"
  2351  					}
  2352  				},
  2353  				"spec": {
  2354  					"hostname": "test-hostname",
  2355  					"containers": [{
  2356  						"name":  "other-container",
  2357  						"image": "nginx:latest",
  2358  					}]
  2359  				}
  2360  			}
  2361  		}
  2362  	}`)
  2363  
  2364  	patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2365  		AbsPath("/apis/apps/v1").
  2366  		Namespace("default").
  2367  		Resource("deployments").
  2368  		Name("deployment-shared-map-item-removal").
  2369  		Param("fieldManager", "test_applier").
  2370  		Body(apply).
  2371  		Do(context.TODO()).
  2372  		Get()
  2373  	if err != nil {
  2374  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2375  	}
  2376  
  2377  	// ensure the container is deleted even though a controller updated a field of the container
  2378  	deployment, ok := patched.(*appsv1.Deployment)
  2379  	if !ok {
  2380  		t.Fatalf("Failed to convert response object to Deployment")
  2381  	}
  2382  	if len(deployment.Spec.Template.Spec.Containers) != 1 {
  2383  		t.Fatalf("Expected 1 container after apply, got %d", len(deployment.Spec.Template.Spec.Containers))
  2384  	}
  2385  	if deployment.Spec.Template.Spec.Containers[0].Name != "other-container" {
  2386  		t.Fatalf("Expected container to be named \"other-container\" but got %s", deployment.Spec.Template.Spec.Containers[0].Name)
  2387  	}
  2388  }
  2389  
  2390  // TestDefaultMissingKeys makes sure that the missing keys default is used when merging.
  2391  func TestDefaultMissingKeys(t *testing.T) {
  2392  	client, closeFn := setup(t)
  2393  	defer closeFn()
  2394  
  2395  	// Applier creates a deployment with containerPort but no protocol
  2396  	apply := []byte(`{
  2397  		"apiVersion": "apps/v1",
  2398  		"kind": "Deployment",
  2399  		"metadata": {
  2400  			"name": "deployment-shared-map-item-removal",
  2401  			"labels": {"app": "nginx"}
  2402  		},
  2403  		"spec": {
  2404  			"selector": {
  2405  				"matchLabels": {
  2406  					"app": "nginx"
  2407  				}
  2408  			},
  2409  			"template": {
  2410  				"metadata": {
  2411  					"labels": {
  2412  						"app": "nginx"
  2413  					}
  2414  				},
  2415  				"spec": {
  2416  					"containers": [{
  2417  						"name":  "nginx",
  2418  						"image": "nginx:latest",
  2419  						"ports": [{
  2420  							"name": "foo",
  2421  							"containerPort": 80
  2422  						}]
  2423  					}]
  2424  				}
  2425  			}
  2426  		}
  2427  	}`)
  2428  
  2429  	_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2430  		AbsPath("/apis/apps/v1").
  2431  		Namespace("default").
  2432  		Resource("deployments").
  2433  		Name("deployment-shared-map-item-removal").
  2434  		Param("fieldManager", "test_applier").
  2435  		Body(apply).
  2436  		Do(context.TODO()).
  2437  		Get()
  2438  	if err != nil {
  2439  		t.Fatalf("Failed to create object using Apply patch: %v", err)
  2440  	}
  2441  
  2442  	// Applier updates the name, and uses the protocol, we should get a conflict.
  2443  	apply = []byte(`{
  2444  		"apiVersion": "apps/v1",
  2445  		"kind": "Deployment",
  2446  		"metadata": {
  2447  			"name": "deployment-shared-map-item-removal",
  2448  			"labels": {"app": "nginx"}
  2449  		},
  2450  		"spec": {
  2451  			"selector": {
  2452  				"matchLabels": {
  2453  					"app": "nginx"
  2454  				}
  2455  			},
  2456  			"template": {
  2457  				"metadata": {
  2458  					"labels": {
  2459  						"app": "nginx"
  2460  					}
  2461  				},
  2462  				"spec": {
  2463  					"containers": [{
  2464  						"name":  "nginx",
  2465  						"image": "nginx:latest",
  2466  						"ports": [{
  2467  							"name": "bar",
  2468  							"containerPort": 80,
  2469  							"protocol": "TCP"
  2470  						}]
  2471  					}]
  2472  				}
  2473  			}
  2474  		}
  2475  	}`)
  2476  	patched, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2477  		AbsPath("/apis/apps/v1").
  2478  		Namespace("default").
  2479  		Resource("deployments").
  2480  		Name("deployment-shared-map-item-removal").
  2481  		Param("fieldManager", "test_applier_conflict").
  2482  		Body(apply).
  2483  		Do(context.TODO()).
  2484  		Get()
  2485  	if err == nil {
  2486  		t.Fatalf("Expecting to get conflicts when a different applier updates existing list item, got no error: %s", patched)
  2487  	}
  2488  	status, ok := err.(*apierrors.StatusError)
  2489  	if !ok {
  2490  		t.Fatalf("Expecting to get conflicts as API error")
  2491  	}
  2492  	if len(status.Status().Details.Causes) != 1 {
  2493  		t.Fatalf("Expecting to get one conflict when a different applier updates existing list item, got: %v", status.Status().Details.Causes)
  2494  	}
  2495  }
  2496  
  2497  var podBytes = []byte(`
  2498  apiVersion: v1
  2499  kind: Pod
  2500  metadata:
  2501    labels:
  2502      app: some-app
  2503      plugin1: some-value
  2504      plugin2: some-value
  2505      plugin3: some-value
  2506      plugin4: some-value
  2507    name: some-name
  2508    namespace: default
  2509    ownerReferences:
  2510    - apiVersion: apps/v1
  2511      blockOwnerDeletion: true
  2512      controller: true
  2513      kind: ReplicaSet
  2514      name: some-name
  2515      uid: 0a9d2b9e-779e-11e7-b422-42010a8001be
  2516  spec:
  2517    containers:
  2518    - args:
  2519      - one
  2520      - two
  2521      - three
  2522      - four
  2523      - five
  2524      - six
  2525      - seven
  2526      - eight
  2527      - nine
  2528      env:
  2529      - name: VAR_3
  2530        valueFrom:
  2531          secretKeyRef:
  2532            key: some-other-key
  2533            name: some-oher-name
  2534      - name: VAR_2
  2535        valueFrom:
  2536          secretKeyRef:
  2537            key: other-key
  2538            name: other-name
  2539      - name: VAR_1
  2540        valueFrom:
  2541          secretKeyRef:
  2542            key: some-key
  2543            name: some-name
  2544      image: some-image-name
  2545      imagePullPolicy: IfNotPresent
  2546      name: some-name
  2547      resources:
  2548        requests:
  2549          cpu: "0"
  2550      terminationMessagePath: /dev/termination-log
  2551      terminationMessagePolicy: File
  2552      volumeMounts:
  2553      - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
  2554        name: default-token-hu5jz
  2555        readOnly: true
  2556    dnsPolicy: ClusterFirst
  2557    nodeName: node-name
  2558    priority: 0
  2559    restartPolicy: Always
  2560    schedulerName: default-scheduler
  2561    securityContext: {}
  2562    serviceAccount: default
  2563    serviceAccountName: default
  2564    terminationGracePeriodSeconds: 30
  2565    tolerations:
  2566    - effect: NoExecute
  2567      key: node.kubernetes.io/not-ready
  2568      operator: Exists
  2569      tolerationSeconds: 300
  2570    - effect: NoExecute
  2571      key: node.kubernetes.io/unreachable
  2572      operator: Exists
  2573      tolerationSeconds: 300
  2574    volumes:
  2575    - name: default-token-hu5jz
  2576      secret:
  2577        defaultMode: 420
  2578        secretName: default-token-hu5jz
  2579  status:
  2580    conditions:
  2581    - lastProbeTime: null
  2582      lastTransitionTime: "2019-07-08T09:31:18Z"
  2583      status: "True"
  2584      type: Initialized
  2585    - lastProbeTime: null
  2586      lastTransitionTime: "2019-07-08T09:41:59Z"
  2587      status: "True"
  2588      type: Ready
  2589    - lastProbeTime: null
  2590      lastTransitionTime: null
  2591      status: "True"
  2592      type: ContainersReady
  2593    - lastProbeTime: null
  2594      lastTransitionTime: "2019-07-08T09:31:18Z"
  2595      status: "True"
  2596      type: PodScheduled
  2597    containerStatuses:
  2598    - containerID: docker://885e82a1ed0b7356541bb410a0126921ac42439607c09875cd8097dd5d7b5376
  2599      image: some-image-name
  2600      imageID: docker-pullable://some-image-id
  2601      lastState:
  2602        terminated:
  2603          containerID: docker://d57290f9e00fad626b20d2dd87a3cf69bbc22edae07985374f86a8b2b4e39565
  2604          exitCode: 255
  2605          finishedAt: "2019-07-08T09:39:09Z"
  2606          reason: Error
  2607          startedAt: "2019-07-08T09:38:54Z"
  2608      name: name
  2609      ready: true
  2610      restartCount: 6
  2611      state:
  2612        running:
  2613          startedAt: "2019-07-08T09:41:59Z"
  2614    hostIP: 10.0.0.1
  2615    phase: Running
  2616    podIP: 10.0.0.1
  2617    qosClass: BestEffort
  2618    startTime: "2019-07-08T09:31:18Z"
  2619  `)
  2620  
  2621  func decodePod(podBytes []byte) v1.Pod {
  2622  	pod := v1.Pod{}
  2623  	err := yaml.Unmarshal(podBytes, &pod)
  2624  	if err != nil {
  2625  		panic(err)
  2626  	}
  2627  	return pod
  2628  }
  2629  
  2630  func encodePod(pod v1.Pod) []byte {
  2631  	podBytes, err := yaml.Marshal(pod)
  2632  	if err != nil {
  2633  		panic(err)
  2634  	}
  2635  	return podBytes
  2636  }
  2637  
  2638  func getPodBytesWhenEnabled(b *testing.B, pod v1.Pod, format string) []byte {
  2639  	client, closeFn := setup(b)
  2640  	defer closeFn()
  2641  	flag.Lookup("v").Value.Set("0")
  2642  
  2643  	pod.Name = "size-pod"
  2644  	podB, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2645  		Name(pod.Name).
  2646  		Namespace("default").
  2647  		Param("fieldManager", "apply_test").
  2648  		Resource("pods").
  2649  		SetHeader("Accept", format).
  2650  		Body(encodePod(pod)).DoRaw(context.TODO())
  2651  	if err != nil {
  2652  		b.Fatalf("Failed to create object: %#v", err)
  2653  	}
  2654  	return podB
  2655  }
  2656  
  2657  func BenchmarkServerSideApply(b *testing.B) {
  2658  	podBytesWhenEnabled := getPodBytesWhenEnabled(b, decodePod(podBytes), "application/yaml")
  2659  
  2660  	client, closeFn := setup(b)
  2661  	defer closeFn()
  2662  	flag.Lookup("v").Value.Set("0")
  2663  
  2664  	benchAll(b, client, decodePod(podBytesWhenEnabled))
  2665  }
  2666  
  2667  func benchAll(b *testing.B, client clientset.Interface, pod v1.Pod) {
  2668  	// Make sure pod is ready to post
  2669  	pod.ObjectMeta.CreationTimestamp = metav1.Time{}
  2670  	pod.ObjectMeta.ResourceVersion = ""
  2671  	pod.ObjectMeta.UID = ""
  2672  
  2673  	// Create pod for repeated-updates
  2674  	pod.Name = "repeated-pod"
  2675  	_, err := client.CoreV1().RESTClient().Post().
  2676  		Namespace("default").
  2677  		Resource("pods").
  2678  		SetHeader("Content-Type", "application/yaml").
  2679  		Body(encodePod(pod)).Do(context.TODO()).Get()
  2680  	if err != nil {
  2681  		b.Fatalf("Failed to create object: %v", err)
  2682  	}
  2683  
  2684  	b.Run("List1", benchListPod(client, pod, 1))
  2685  	b.Run("List20", benchListPod(client, pod, 20))
  2686  	b.Run("List200", benchListPod(client, pod, 200))
  2687  	b.Run("List2000", benchListPod(client, pod, 2000))
  2688  
  2689  	b.Run("RepeatedUpdates", benchRepeatedUpdate(client, "repeated-pod"))
  2690  	b.Run("Post1", benchPostPod(client, pod, 1))
  2691  	b.Run("Post10", benchPostPod(client, pod, 10))
  2692  	b.Run("Post50", benchPostPod(client, pod, 50))
  2693  }
  2694  
  2695  func benchPostPod(client clientset.Interface, pod v1.Pod, parallel int) func(*testing.B) {
  2696  	return func(b *testing.B) {
  2697  		b.ResetTimer()
  2698  		b.ReportAllocs()
  2699  		for i := 0; i < b.N; i++ {
  2700  			c := make(chan error)
  2701  			for j := 0; j < parallel; j++ {
  2702  				j := j
  2703  				i := i
  2704  				go func(pod v1.Pod) {
  2705  					pod.Name = fmt.Sprintf("post%d-%d-%d-%d", parallel, b.N, j, i)
  2706  					_, err := client.CoreV1().RESTClient().Post().
  2707  						Namespace("default").
  2708  						Resource("pods").
  2709  						SetHeader("Content-Type", "application/yaml").
  2710  						Body(encodePod(pod)).Do(context.TODO()).Get()
  2711  					c <- err
  2712  				}(pod)
  2713  			}
  2714  			for j := 0; j < parallel; j++ {
  2715  				err := <-c
  2716  				if err != nil {
  2717  					b.Fatal(err)
  2718  				}
  2719  			}
  2720  			close(c)
  2721  		}
  2722  	}
  2723  }
  2724  
  2725  func createNamespace(client clientset.Interface, name string) error {
  2726  	namespace := v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}
  2727  	namespaceBytes, err := yaml.Marshal(namespace)
  2728  	if err != nil {
  2729  		return fmt.Errorf("Failed to marshal namespace: %v", err)
  2730  	}
  2731  	_, err = client.CoreV1().RESTClient().Get().
  2732  		Resource("namespaces").
  2733  		SetHeader("Content-Type", "application/yaml").
  2734  		Body(namespaceBytes).Do(context.TODO()).Get()
  2735  	if err != nil {
  2736  		return fmt.Errorf("Failed to create namespace: %v", err)
  2737  	}
  2738  	return nil
  2739  }
  2740  
  2741  func benchListPod(client clientset.Interface, pod v1.Pod, num int) func(*testing.B) {
  2742  	return func(b *testing.B) {
  2743  		namespace := fmt.Sprintf("get-%d-%d", num, b.N)
  2744  		if err := createNamespace(client, namespace); err != nil {
  2745  			b.Fatal(err)
  2746  		}
  2747  		// Create pods
  2748  		for i := 0; i < num; i++ {
  2749  			pod.Name = fmt.Sprintf("get-%d-%d", b.N, i)
  2750  			pod.Namespace = namespace
  2751  			_, err := client.CoreV1().RESTClient().Post().
  2752  				Namespace(namespace).
  2753  				Resource("pods").
  2754  				SetHeader("Content-Type", "application/yaml").
  2755  				Body(encodePod(pod)).Do(context.TODO()).Get()
  2756  			if err != nil {
  2757  				b.Fatalf("Failed to create object: %v", err)
  2758  			}
  2759  		}
  2760  
  2761  		b.ResetTimer()
  2762  		b.ReportAllocs()
  2763  		for i := 0; i < b.N; i++ {
  2764  			_, err := client.CoreV1().RESTClient().Get().
  2765  				Namespace(namespace).
  2766  				Resource("pods").
  2767  				SetHeader("Accept", "application/vnd.kubernetes.protobuf").
  2768  				Do(context.TODO()).Get()
  2769  			if err != nil {
  2770  				b.Fatalf("Failed to patch object: %v", err)
  2771  			}
  2772  		}
  2773  	}
  2774  }
  2775  
  2776  func benchRepeatedUpdate(client clientset.Interface, podName string) func(*testing.B) {
  2777  	return func(b *testing.B) {
  2778  		b.ResetTimer()
  2779  		b.ReportAllocs()
  2780  		for i := 0; i < b.N; i++ {
  2781  			_, err := client.CoreV1().RESTClient().Patch(types.JSONPatchType).
  2782  				Namespace("default").
  2783  				Resource("pods").
  2784  				Name(podName).
  2785  				Body([]byte(fmt.Sprintf(`[{"op": "replace", "path": "/spec/containers/0/image", "value": "image%d"}]`, i))).Do(context.TODO()).Get()
  2786  			if err != nil {
  2787  				b.Fatalf("Failed to patch object: %v", err)
  2788  			}
  2789  		}
  2790  	}
  2791  }
  2792  
  2793  func TestUpgradeClientSideToServerSideApply(t *testing.T) {
  2794  	client, closeFn := setup(t)
  2795  	defer closeFn()
  2796  
  2797  	obj := []byte(`
  2798  apiVersion: apps/v1
  2799  kind: Deployment
  2800  metadata:
  2801    name: my-deployment
  2802    annotations:
  2803      "kubectl.kubernetes.io/last-applied-configuration": |
  2804        {"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"my-deployment","labels":{"app":"my-app"}},"spec":{"replicas": 3,"template":{"metadata":{"labels":{"app":"my-app"}},"spec":{"containers":[{"name":"my-c","image":"my-image"}]}}}}
  2805    labels:
  2806      app: my-app
  2807  spec:
  2808    replicas: 100000
  2809    selector:
  2810      matchLabels:
  2811        app: my-app
  2812    template:
  2813      metadata:
  2814        labels:
  2815          app: my-app
  2816      spec:
  2817        containers:
  2818        - name: my-c
  2819          image: my-image
  2820  `)
  2821  
  2822  	deployment, err := yamlutil.ToJSON(obj)
  2823  	if err != nil {
  2824  		t.Fatalf("Failed marshal yaml: %v", err)
  2825  	}
  2826  
  2827  	_, err = client.CoreV1().RESTClient().Post().
  2828  		AbsPath("/apis/apps/v1").
  2829  		Namespace("default").
  2830  		Resource("deployments").
  2831  		Body(deployment).Do(context.TODO()).Get()
  2832  	if err != nil {
  2833  		t.Fatalf("Failed to create object: %v", err)
  2834  	}
  2835  
  2836  	obj = []byte(`
  2837  apiVersion: apps/v1
  2838  kind: Deployment
  2839  metadata:
  2840    name: my-deployment
  2841    labels:
  2842      app: my-new-label
  2843  spec:
  2844    replicas: 3 # expect conflict
  2845    template:
  2846      metadata:
  2847        labels:
  2848          app: my-app
  2849      spec:
  2850        containers:
  2851        - name: my-c
  2852          image: my-image
  2853  `)
  2854  
  2855  	deployment, err = yamlutil.ToJSON(obj)
  2856  	if err != nil {
  2857  		t.Fatalf("Failed marshal yaml: %v", err)
  2858  	}
  2859  
  2860  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2861  		AbsPath("/apis/apps/v1").
  2862  		Namespace("default").
  2863  		Resource("deployments").
  2864  		Name("my-deployment").
  2865  		Param("fieldManager", "kubectl").
  2866  		Body(deployment).
  2867  		Do(context.TODO()).
  2868  		Get()
  2869  	if !apierrors.IsConflict(err) {
  2870  		t.Fatalf("Expected conflict error but got: %v", err)
  2871  	}
  2872  
  2873  	obj = []byte(`
  2874  apiVersion: apps/v1
  2875  kind: Deployment
  2876  metadata:
  2877    name: my-deployment
  2878    labels:
  2879      app: my-new-label
  2880  spec:
  2881    template:
  2882      metadata:
  2883        labels:
  2884          app: my-app
  2885      spec:
  2886        containers:
  2887        - name: my-c
  2888          image: my-image-new
  2889  `)
  2890  
  2891  	deployment, err = yamlutil.ToJSON(obj)
  2892  	if err != nil {
  2893  		t.Fatalf("Failed marshal yaml: %v", err)
  2894  	}
  2895  
  2896  	_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
  2897  		AbsPath("/apis/apps/v1").
  2898  		Namespace("default").
  2899  		Resource("deployments").
  2900  		Name("my-deployment").
  2901  		Param("fieldManager", "kubectl").
  2902  		Body(deployment).
  2903  		Do(context.TODO()).
  2904  		Get()
  2905  	if err != nil {
  2906  		t.Fatalf("Failed to apply object: %v", err)
  2907  	}
  2908  
  2909  	deploymentObj, err := client.AppsV1().Deployments("default").Get(context.TODO(), "my-deployment", metav1.GetOptions{})
  2910  	if err != nil {
  2911  		t.Fatalf("Failed to get object: %v", err)
  2912  	}
  2913  	if *deploymentObj.Spec.Replicas != 100000 {
  2914  		t.Fatalf("expected to get obj with replicas %d, but got %d", 100000, *deploymentObj.Spec.Replicas)
  2915  	}
  2916  	if deploymentObj.Spec.Template.Spec.Containers[0].Image != "my-image-new" {
  2917  		t.Fatalf("expected to get obj with image %s, but got %s", "my-image-new", deploymentObj.Spec.Template.Spec.Containers[0].Image)
  2918  	}
  2919  }
  2920  
  2921  func TestRenamingAppliedFieldManagers(t *testing.T) {
  2922  	client, closeFn := setup(t)
  2923  	defer closeFn()
  2924  
  2925  	// Creating an object
  2926  	podBytes := []byte(`{
  2927  		"apiVersion": "v1",
  2928  		"kind": "Pod",
  2929  		"metadata": {
  2930  			"name": "just-a-pod",
  2931  			"labels": {
  2932  				"a": "one"
  2933  			}
  2934  		},
  2935  		"spec": {
  2936  			"containers": [{
  2937  				"name":  "test-container-a",
  2938  				"image": "test-image-one"
  2939  			}]
  2940  		}
  2941  	}`)
  2942  	_, err := client.CoreV1().RESTClient().
  2943  		Patch(types.ApplyPatchType).
  2944  		Namespace("default").
  2945  		Param("fieldManager", "multi_manager_one").
  2946  		Resource("pods").
  2947  		Name("just-a-pod").
  2948  		Body(podBytes).
  2949  		Do(context.TODO()).
  2950  		Get()
  2951  	if err != nil {
  2952  		t.Fatalf("Failed to apply: %v", err)
  2953  	}
  2954  	_, err = client.CoreV1().RESTClient().
  2955  		Patch(types.ApplyPatchType).
  2956  		Namespace("default").
  2957  		Param("fieldManager", "multi_manager_two").
  2958  		Resource("pods").
  2959  		Name("just-a-pod").
  2960  		Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"b":"two"}}}`)).
  2961  		Do(context.TODO()).
  2962  		Get()
  2963  	if err != nil {
  2964  		t.Fatalf("Failed to apply: %v", err)
  2965  	}
  2966  
  2967  	pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
  2968  	if err != nil {
  2969  		t.Fatalf("Failed to get object: %v", err)
  2970  	}
  2971  	expectedLabels := map[string]string{
  2972  		"a": "one",
  2973  		"b": "two",
  2974  	}
  2975  	if !reflect.DeepEqual(pod.Labels, expectedLabels) {
  2976  		t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
  2977  	}
  2978  
  2979  	managedFields := pod.GetManagedFields()
  2980  	for i := range managedFields {
  2981  		managedFields[i].Manager = "multi_manager"
  2982  	}
  2983  	pod.SetManagedFields(managedFields)
  2984  
  2985  	obj, err := client.CoreV1().RESTClient().
  2986  		Put().
  2987  		Namespace("default").
  2988  		Resource("pods").
  2989  		Name("just-a-pod").
  2990  		Body(pod).
  2991  		Do(context.TODO()).
  2992  		Get()
  2993  	if err != nil {
  2994  		t.Fatalf("Failed to update: %v", err)
  2995  	}
  2996  
  2997  	accessor, err := meta.Accessor(obj)
  2998  	if err != nil {
  2999  		t.Fatalf("Failed to get meta accessor for object: %v", err)
  3000  	}
  3001  	managedFields = accessor.GetManagedFields()
  3002  	if len(managedFields) != 1 {
  3003  		t.Fatalf("Expected object to have 1 managed fields entry, got: %d", len(managedFields))
  3004  	}
  3005  	entry := managedFields[0]
  3006  	if entry.Manager != "multi_manager" || entry.Operation != "Apply" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
  3007  		t.Fatalf(`Unexpected entry, got: %v`, entry)
  3008  	}
  3009  }
  3010  
  3011  func TestRenamingUpdatedFieldManagers(t *testing.T) {
  3012  	client, closeFn := setup(t)
  3013  	defer closeFn()
  3014  
  3015  	// Creating an object
  3016  	podBytes := []byte(`{
  3017  		"apiVersion": "v1",
  3018  		"kind": "Pod",
  3019  		"metadata": {
  3020  			"name": "just-a-pod"
  3021  		},
  3022  		"spec": {
  3023  			"containers": [{
  3024  				"name":  "test-container-a",
  3025  				"image": "test-image-one"
  3026  			}]
  3027  		}
  3028  	}`)
  3029  	_, err := client.CoreV1().RESTClient().
  3030  		Patch(types.ApplyPatchType).
  3031  		Namespace("default").
  3032  		Param("fieldManager", "first").
  3033  		Resource("pods").
  3034  		Name("just-a-pod").
  3035  		Body(podBytes).
  3036  		Do(context.TODO()).
  3037  		Get()
  3038  	if err != nil {
  3039  		t.Fatalf("Failed to create object: %v", err)
  3040  	}
  3041  
  3042  	_, err = client.CoreV1().RESTClient().
  3043  		Patch(types.MergePatchType).
  3044  		Namespace("default").
  3045  		Param("fieldManager", "multi_manager_one").
  3046  		Resource("pods").
  3047  		Name("just-a-pod").
  3048  		Body([]byte(`{"metadata":{"labels":{"a":"one"}}}`)).
  3049  		Do(context.TODO()).
  3050  		Get()
  3051  	if err != nil {
  3052  		t.Fatalf("Failed to update: %v", err)
  3053  	}
  3054  	_, err = client.CoreV1().RESTClient().
  3055  		Patch(types.MergePatchType).
  3056  		Namespace("default").
  3057  		Param("fieldManager", "multi_manager_two").
  3058  		Resource("pods").
  3059  		Name("just-a-pod").
  3060  		Body([]byte(`{"metadata":{"labels":{"b":"two"}}}`)).
  3061  		Do(context.TODO()).
  3062  		Get()
  3063  	if err != nil {
  3064  		t.Fatalf("Failed to update: %v", err)
  3065  	}
  3066  
  3067  	pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
  3068  	if err != nil {
  3069  		t.Fatalf("Failed to get object: %v", err)
  3070  	}
  3071  	expectedLabels := map[string]string{
  3072  		"a": "one",
  3073  		"b": "two",
  3074  	}
  3075  	if !reflect.DeepEqual(pod.Labels, expectedLabels) {
  3076  		t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
  3077  	}
  3078  
  3079  	managedFields := pod.GetManagedFields()
  3080  	for i := range managedFields {
  3081  		managedFields[i].Manager = "multi_manager"
  3082  	}
  3083  	pod.SetManagedFields(managedFields)
  3084  
  3085  	obj, err := client.CoreV1().RESTClient().
  3086  		Put().
  3087  		Namespace("default").
  3088  		Resource("pods").
  3089  		Name("just-a-pod").
  3090  		Body(pod).
  3091  		Do(context.TODO()).
  3092  		Get()
  3093  	if err != nil {
  3094  		t.Fatalf("Failed to update: %v", err)
  3095  	}
  3096  
  3097  	accessor, err := meta.Accessor(obj)
  3098  	if err != nil {
  3099  		t.Fatalf("Failed to get meta accessor for object: %v", err)
  3100  	}
  3101  	managedFields = accessor.GetManagedFields()
  3102  	if len(managedFields) != 2 {
  3103  		t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
  3104  	}
  3105  	entry := managedFields[1]
  3106  	if entry.Manager != "multi_manager" || entry.Operation != "Update" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
  3107  		t.Fatalf(`Unexpected entry, got: %v`, entry)
  3108  	}
  3109  }
  3110  
  3111  func TestDroppingSubresourceField(t *testing.T) {
  3112  	client, closeFn := setup(t)
  3113  	defer closeFn()
  3114  
  3115  	// Creating an object
  3116  	podBytes := []byte(`{
  3117  		"apiVersion": "v1",
  3118  		"kind": "Pod",
  3119  		"metadata": {
  3120  			"name": "just-a-pod"
  3121  		},
  3122  		"spec": {
  3123  			"containers": [{
  3124  				"name":  "test-container-a",
  3125  				"image": "test-image-one"
  3126  			}]
  3127  		}
  3128  	}`)
  3129  	_, err := client.CoreV1().RESTClient().
  3130  		Patch(types.ApplyPatchType).
  3131  		Namespace("default").
  3132  		Param("fieldManager", "first").
  3133  		Resource("pods").
  3134  		Name("just-a-pod").
  3135  		Body(podBytes).
  3136  		Do(context.TODO()).
  3137  		Get()
  3138  	if err != nil {
  3139  		t.Fatalf("Failed to create object: %v", err)
  3140  	}
  3141  
  3142  	_, err = client.CoreV1().RESTClient().
  3143  		Patch(types.ApplyPatchType).
  3144  		Namespace("default").
  3145  		Param("fieldManager", "label_manager").
  3146  		Resource("pods").
  3147  		Name("just-a-pod").
  3148  		Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"a":"one"}}}`)).
  3149  		Do(context.TODO()).
  3150  		Get()
  3151  	if err != nil {
  3152  		t.Fatalf("Failed to apply: %v", err)
  3153  	}
  3154  	_, err = client.CoreV1().RESTClient().
  3155  		Patch(types.ApplyPatchType).
  3156  		Namespace("default").
  3157  		Param("fieldManager", "label_manager").
  3158  		Resource("pods").
  3159  		Name("just-a-pod").
  3160  		SubResource("status").
  3161  		Body([]byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"labels":{"b":"two"}}}`)).
  3162  		Do(context.TODO()).
  3163  		Get()
  3164  	if err != nil {
  3165  		t.Fatalf("Failed to apply: %v", err)
  3166  	}
  3167  
  3168  	pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
  3169  	if err != nil {
  3170  		t.Fatalf("Failed to get object: %v", err)
  3171  	}
  3172  	expectedLabels := map[string]string{
  3173  		"a": "one",
  3174  		"b": "two",
  3175  	}
  3176  	if !reflect.DeepEqual(pod.Labels, expectedLabels) {
  3177  		t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
  3178  	}
  3179  
  3180  	managedFields := pod.GetManagedFields()
  3181  	if len(managedFields) != 3 {
  3182  		t.Fatalf("Expected object to have 3 managed fields entries, got: %d", len(managedFields))
  3183  	}
  3184  	if managedFields[1].Manager != "label_manager" || managedFields[1].Operation != "Apply" || managedFields[1].Subresource != "" {
  3185  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
  3186  	}
  3187  	if managedFields[2].Manager != "label_manager" || managedFields[2].Operation != "Apply" || managedFields[2].Subresource != "status" {
  3188  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[2])
  3189  	}
  3190  
  3191  	for i := range managedFields {
  3192  		managedFields[i].Subresource = ""
  3193  	}
  3194  	pod.SetManagedFields(managedFields)
  3195  
  3196  	obj, err := client.CoreV1().RESTClient().
  3197  		Put().
  3198  		Namespace("default").
  3199  		Resource("pods").
  3200  		Name("just-a-pod").
  3201  		Body(pod).
  3202  		Do(context.TODO()).
  3203  		Get()
  3204  	if err != nil {
  3205  		t.Fatalf("Failed to update: %v", err)
  3206  	}
  3207  
  3208  	accessor, err := meta.Accessor(obj)
  3209  	if err != nil {
  3210  		t.Fatalf("Failed to get meta accessor for object: %v", err)
  3211  	}
  3212  	managedFields = accessor.GetManagedFields()
  3213  	if len(managedFields) != 2 {
  3214  		t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
  3215  	}
  3216  	entry := managedFields[1]
  3217  	if entry.Manager != "label_manager" || entry.Operation != "Apply" || string(entry.FieldsV1.Raw) != `{"f:metadata":{"f:labels":{"f:b":{}}}}` {
  3218  		t.Fatalf(`Unexpected entry, got: %v`, entry)
  3219  	}
  3220  }
  3221  
  3222  func TestDroppingSubresourceFromSpecField(t *testing.T) {
  3223  	client, closeFn := setup(t)
  3224  	defer closeFn()
  3225  
  3226  	// Creating an object
  3227  	podBytes := []byte(`{
  3228  		"apiVersion": "v1",
  3229  		"kind": "Pod",
  3230  		"metadata": {
  3231  			"name": "just-a-pod"
  3232  		},
  3233  		"spec": {
  3234  			"containers": [{
  3235  				"name":  "test-container-a",
  3236  				"image": "test-image-one"
  3237  			}]
  3238  		}
  3239  	}`)
  3240  	_, err := client.CoreV1().RESTClient().
  3241  		Patch(types.ApplyPatchType).
  3242  		Namespace("default").
  3243  		Param("fieldManager", "first").
  3244  		Resource("pods").
  3245  		Name("just-a-pod").
  3246  		Body(podBytes).
  3247  		Do(context.TODO()).
  3248  		Get()
  3249  	if err != nil {
  3250  		t.Fatalf("Failed to create object: %v", err)
  3251  	}
  3252  
  3253  	_, err = client.CoreV1().RESTClient().
  3254  		Patch(types.MergePatchType).
  3255  		Namespace("default").
  3256  		Param("fieldManager", "manager").
  3257  		Resource("pods").
  3258  		Name("just-a-pod").
  3259  		Body([]byte(`{"metadata":{"labels":{"a":"two"}}}`)).
  3260  		Do(context.TODO()).
  3261  		Get()
  3262  	if err != nil {
  3263  		t.Fatalf("Failed to update: %v", err)
  3264  	}
  3265  
  3266  	_, err = client.CoreV1().RESTClient().
  3267  		Patch(types.MergePatchType).
  3268  		Namespace("default").
  3269  		Param("fieldManager", "manager").
  3270  		Resource("pods").
  3271  		SubResource("status").
  3272  		Name("just-a-pod").
  3273  		Body([]byte(`{"status":{"phase":"Running"}}`)).
  3274  		Do(context.TODO()).
  3275  		Get()
  3276  	if err != nil {
  3277  		t.Fatalf("Failed to apply: %v", err)
  3278  	}
  3279  
  3280  	pod, err := client.CoreV1().Pods("default").Get(context.TODO(), "just-a-pod", metav1.GetOptions{})
  3281  	if err != nil {
  3282  		t.Fatalf("Failed to get object: %v", err)
  3283  	}
  3284  	expectedLabels := map[string]string{"a": "two"}
  3285  	if !reflect.DeepEqual(pod.Labels, expectedLabels) {
  3286  		t.Fatalf("Expected labels to be %v, but got %v", expectedLabels, pod.Labels)
  3287  	}
  3288  	if pod.Status.Phase != v1.PodRunning {
  3289  		t.Fatalf("Expected phase to be %q, but got %q", v1.PodRunning, pod.Status.Phase)
  3290  	}
  3291  
  3292  	managedFields := pod.GetManagedFields()
  3293  	if len(managedFields) != 3 {
  3294  		t.Fatalf("Expected object to have 3 managed fields entries, got: %d", len(managedFields))
  3295  	}
  3296  	if managedFields[1].Manager != "manager" || managedFields[1].Operation != "Update" || managedFields[1].Subresource != "" {
  3297  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
  3298  	}
  3299  	if managedFields[2].Manager != "manager" || managedFields[2].Operation != "Update" || managedFields[2].Subresource != "status" {
  3300  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[2])
  3301  	}
  3302  
  3303  	for i := range managedFields {
  3304  		managedFields[i].Subresource = ""
  3305  	}
  3306  	pod.SetManagedFields(managedFields)
  3307  
  3308  	obj, err := client.CoreV1().RESTClient().
  3309  		Put().
  3310  		Namespace("default").
  3311  		Resource("pods").
  3312  		Name("just-a-pod").
  3313  		Body(pod).
  3314  		Do(context.TODO()).
  3315  		Get()
  3316  	if err != nil {
  3317  		t.Fatalf("Failed to update: %v", err)
  3318  	}
  3319  
  3320  	accessor, err := meta.Accessor(obj)
  3321  	if err != nil {
  3322  		t.Fatalf("Failed to get meta accessor for object: %v", err)
  3323  	}
  3324  	managedFields = accessor.GetManagedFields()
  3325  	if len(managedFields) != 2 {
  3326  		t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
  3327  	}
  3328  	entry := managedFields[1]
  3329  	if entry.Manager != "manager" || entry.Operation != "Update" || string(entry.FieldsV1.Raw) != `{"f:status":{"f:phase":{}}}` {
  3330  		t.Fatalf(`Unexpected entry, got: %v`, entry)
  3331  	}
  3332  }
  3333  
  3334  func TestSubresourceField(t *testing.T) {
  3335  	client, closeFn := setup(t)
  3336  	defer closeFn()
  3337  
  3338  	// Creating a deployment
  3339  	deploymentBytes := []byte(`{
  3340  		"apiVersion": "apps/v1",
  3341  		"kind": "Deployment",
  3342  		"metadata": {
  3343  			"name": "deployment",
  3344  			"labels": {"app": "nginx"}
  3345  		},
  3346  		"spec": {
  3347  			"replicas": 3,
  3348  			"selector": {
  3349  				"matchLabels": {
  3350  					 "app": "nginx"
  3351  				}
  3352  			},
  3353  			"template": {
  3354  				"metadata": {
  3355  					"labels": {
  3356  						"app": "nginx"
  3357  					}
  3358  				},
  3359  				"spec": {
  3360  					"containers": [{
  3361  						"name":  "nginx",
  3362  						"image": "nginx:latest"
  3363  					}]
  3364  				}
  3365  			}
  3366  		}
  3367  	}`)
  3368  	_, err := client.CoreV1().RESTClient().
  3369  		Patch(types.ApplyPatchType).
  3370  		AbsPath("/apis/apps/v1").
  3371  		Namespace("default").
  3372  		Resource("deployments").
  3373  		Name("deployment").
  3374  		Param("fieldManager", "manager").
  3375  		Body(deploymentBytes).Do(context.TODO()).Get()
  3376  	if err != nil {
  3377  		t.Fatalf("Failed to apply object: %v", err)
  3378  	}
  3379  
  3380  	_, err = client.CoreV1().RESTClient().
  3381  		Patch(types.MergePatchType).
  3382  		AbsPath("/apis/apps/v1").
  3383  		Namespace("default").
  3384  		Resource("deployments").
  3385  		SubResource("scale").
  3386  		Name("deployment").
  3387  		Body([]byte(`{"spec":{"replicas":32}}`)).
  3388  		Param("fieldManager", "manager").
  3389  		Do(context.TODO()).
  3390  		Get()
  3391  	if err != nil {
  3392  		t.Fatalf("Failed to update status: %v", err)
  3393  	}
  3394  
  3395  	deployment, err := client.AppsV1().Deployments("default").Get(context.TODO(), "deployment", metav1.GetOptions{})
  3396  	if err != nil {
  3397  		t.Fatalf("Failed to get object: %v", err)
  3398  	}
  3399  
  3400  	managedFields := deployment.GetManagedFields()
  3401  	if len(managedFields) != 2 {
  3402  		t.Fatalf("Expected object to have 2 managed fields entries, got: %d", len(managedFields))
  3403  	}
  3404  	if managedFields[0].Manager != "manager" || managedFields[0].Operation != "Apply" || managedFields[0].Subresource != "" {
  3405  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[0])
  3406  	}
  3407  	if managedFields[1].Manager != "manager" ||
  3408  		managedFields[1].Operation != "Update" ||
  3409  		managedFields[1].Subresource != "scale" ||
  3410  		string(managedFields[1].FieldsV1.Raw) != `{"f:spec":{"f:replicas":{}}}` {
  3411  		t.Fatalf(`Unexpected entry, got: %v`, managedFields[1])
  3412  	}
  3413  }
  3414  
  3415  // K8s has a bug introduced in vX.XX.X which changed the treatment of
  3416  // ObjectReferences from granular to atomic. This means that only one manager
  3417  // may own all fields of the ObjectReference. This resulted in a regression
  3418  // for the common use case of user-specified GVK, and machine-populated UID fields.
  3419  //
  3420  // This is a test to show that clusters  affected by this bug before it was fixed
  3421  // do not experience any friction when updating to a version of k8s which marks
  3422  // the fields' management again as granular.
  3423  func TestApplyFormerlyAtomicFields(t *testing.T) {
  3424  	// Start server with our populated ObjectReference. Since it is atomic its
  3425  	// ownership changed when XX popualted the UID after the user specified the
  3426  	// GVKN.
  3427  
  3428  	// 1. Create PersistentVolume with its claimRef owned by
  3429  	//		kube-controller-manager as in v1.22 - 1.24
  3430  	// 2. Attempt to re-apply the original PersistentVolume which does not
  3431  	//		include uid.
  3432  	// 3. Check that:
  3433  	//		a.) The operaiton was successfu;
  3434  	//		b.) The uid is unchanged
  3435  
  3436  	client, closeFn := setup(t)
  3437  	defer closeFn()
  3438  
  3439  	// old PersistentVolume from last version of k8s with its claimRef owned
  3440  	// atomically
  3441  	oldPersistentVolume := []byte(`
  3442  	{
  3443  		"apiVersion": "v1",
  3444  		"kind": "PersistentVolume",
  3445  		"metadata": {
  3446  			"creationTimestamp": "2022-06-08T23:46:32Z",
  3447  			"finalizers": [
  3448  				"kubernetes.io/pv-protection"
  3449  			],
  3450  			"labels": {
  3451  				"type": "local"
  3452  			},
  3453  			"name": "pv-storage",
  3454  			"uid": "112b18f7-fde6-4e48-aa61-f5168bd576b8"
  3455  		},
  3456  		"spec": {
  3457  			"accessModes": [
  3458  				"ReadWriteOnce"
  3459  			],
  3460  			"capacity": {
  3461  				"storage": "16Mi"
  3462  			},
  3463  			"claimRef": {
  3464  				"apiVersion": "v1",
  3465  				"kind": "PersistentVolumeClaim",
  3466  				"name": "pvc-storage",
  3467  				"namespace": "default",
  3468  				"resourceVersion": "15499",
  3469  				"uid": "2018e302-7b12-406c-9fa2-e52535d29e48"
  3470  			},
  3471  			"hostPath": {
  3472  				"path": "“/tmp/mydata",
  3473  				"type": ""
  3474  			},
  3475  			"persistentVolumeReclaimPolicy": "Retain",
  3476  			"volumeMode": "Filesystem"
  3477  		},
  3478  		"status": {
  3479  			"phase": "Bound"
  3480  		}
  3481  	}`)
  3482  
  3483  	managedFieldsUpdate := []byte(`{
  3484  		"apiVersion": "v1",
  3485  		"kind": "PersistentVolume",
  3486  		"metadata": {
  3487  			"name": "pv-storage",
  3488  			"managedFields": [
  3489  				{
  3490  					"apiVersion": "v1",
  3491  					"fieldsType": "FieldsV1",
  3492  					"fieldsV1": {
  3493  						"f:metadata": {
  3494  							"f:labels": {
  3495  								"f:type": {}
  3496  							}
  3497  						},
  3498  						"f:spec": {
  3499  							"f:accessModes": {},
  3500  							"f:capacity": {
  3501  								"f:storage": {}
  3502  							},
  3503  							"f:hostPath": {
  3504  								"f:path": {}
  3505  							},
  3506  							"f:storageClassName": {}
  3507  						}
  3508  					},
  3509  					"manager": "apply_test",
  3510  					"operation": "Apply",
  3511  					"time": "2022-06-08T23:46:32Z"
  3512  				},
  3513  				{
  3514  					"apiVersion": "v1",
  3515  					"fieldsType": "FieldsV1",
  3516  					"fieldsV1": {
  3517  						"f:status": {
  3518  							"f:phase": {}
  3519  						}
  3520  					},
  3521  					"manager": "kube-controller-manager",
  3522  					"operation": "Update",
  3523  					"subresource": "status",
  3524  					"time": "2022-06-08T23:46:32Z"
  3525  				},
  3526  				{
  3527  					"apiVersion": "v1",
  3528  					"fieldsType": "FieldsV1",
  3529  					"fieldsV1": {
  3530  						"f:spec": {
  3531  							"f:claimRef": {}
  3532  						}
  3533  					},
  3534  					"manager": "kube-controller-manager",
  3535  					"operation": "Update",
  3536  					"time": "2022-06-08T23:46:37Z"
  3537  				}
  3538  			]
  3539  		}
  3540  	}`)
  3541  
  3542  	// Re-applies name and namespace
  3543  	originalPV := []byte(`{
  3544  		"kind": "PersistentVolume",
  3545  		"apiVersion": "v1",
  3546  		"metadata": {
  3547  			"labels": {
  3548  				"type": "local"
  3549  			},
  3550  			"name": "pv-storage",
  3551  		},
  3552  		"spec": {
  3553  			"storageClassName": "",
  3554  			"capacity": {
  3555  				"storage": "16Mi"
  3556  			},
  3557  			"accessModes": [
  3558  				"ReadWriteOnce"
  3559  			],
  3560  			"hostPath": {
  3561  				"path": "“/tmp/mydata"
  3562  			},
  3563  			"claimRef": {
  3564  				"name": "pvc-storage",
  3565  				"namespace": "default"
  3566  			}
  3567  		}
  3568  	}`)
  3569  
  3570  	// Create PV
  3571  	originalObj, err := client.CoreV1().RESTClient().
  3572  		Post().
  3573  		Param("fieldManager", "apply_test").
  3574  		Resource("persistentvolumes").
  3575  		Body(oldPersistentVolume).
  3576  		Do(context.TODO()).
  3577  		Get()
  3578  
  3579  	if err != nil {
  3580  		t.Fatalf("Failed to apply object: %v", err)
  3581  	} else if _, ok := originalObj.(*v1.PersistentVolume); !ok {
  3582  		t.Fatalf("returned object is incorrect type: %t", originalObj)
  3583  	}
  3584  
  3585  	// Directly set managed fields to object
  3586  	newObj, err := client.CoreV1().RESTClient().
  3587  		Patch(types.StrategicMergePatchType).
  3588  		Name("pv-storage").
  3589  		Param("fieldManager", "apply_test").
  3590  		Resource("persistentvolumes").
  3591  		Body(managedFieldsUpdate).
  3592  		Do(context.TODO()).
  3593  		Get()
  3594  
  3595  	if err != nil {
  3596  		t.Fatalf("Failed to apply object: %v", err)
  3597  	} else if _, ok := newObj.(*v1.PersistentVolume); !ok {
  3598  		t.Fatalf("returned object is incorrect type: %t", newObj)
  3599  	}
  3600  
  3601  	// Is initialized, attempt to write to fields underneath
  3602  	//	claimRef ObjectReference.
  3603  	newObj, err = client.CoreV1().RESTClient().
  3604  		Patch(types.ApplyPatchType).
  3605  		Name("pv-storage").
  3606  		Param("fieldManager", "apply_test").
  3607  		Resource("persistentvolumes").
  3608  		Body(originalPV).
  3609  		Do(context.TODO()).
  3610  		Get()
  3611  
  3612  	if err != nil {
  3613  		t.Fatalf("Failed to apply object: %v", err)
  3614  	} else if _, ok := newObj.(*v1.PersistentVolume); !ok {
  3615  		t.Fatalf("returned object is incorrect type: %t", newObj)
  3616  	}
  3617  
  3618  	// Test that bug is fixed by showing no error and that uid is not cleared.
  3619  	if !reflect.DeepEqual(originalObj.(*v1.PersistentVolume).Spec.ClaimRef, newObj.(*v1.PersistentVolume).Spec.ClaimRef) {
  3620  		t.Fatalf("claimRef changed unexpectedly")
  3621  	}
  3622  
  3623  	// Expect that we know own name/namespace fields
  3624  	// All other fields unowned
  3625  	// Make sure apply_test now owns claimRef.UID and that kube-controller-manager owns
  3626  	// claimRef (but its ownership is not respected due to new granular structType)
  3627  	managedFields := newObj.(*v1.PersistentVolume).ManagedFields
  3628  	var expectedManagedFields []metav1.ManagedFieldsEntry
  3629  	expectedManagedFieldsString := []byte(`[
  3630  		{
  3631  			"apiVersion": "v1",
  3632  			"fieldsType": "FieldsV1",
  3633  			"fieldsV1": {"f:metadata":{"f:labels":{"f:type":{}}},"f:spec":{"f:accessModes":{},"f:capacity":{"f:storage":{}},"f:claimRef":{"f:name":{},"f:namespace":{}},"f:hostPath":{"f:path":{}},"f:storageClassName":{}}},
  3634  			"manager": "apply_test",
  3635  			"operation": "Apply",
  3636  			"time": "2022-06-08T23:46:32Z"
  3637  		},
  3638  		{
  3639  			"apiVersion": "v1",
  3640  			"fieldsType": "FieldsV1",
  3641  			"fieldsV1": {"f:status":{"f:phase":{}}},
  3642  			"manager": "kube-controller-manager",
  3643  			"operation": "Update",
  3644  			"subresource": "status",
  3645  			"time": "2022-06-08T23:46:32Z"
  3646  		},
  3647  		{
  3648  			"apiVersion": "v1",
  3649  			"fieldsType": "FieldsV1",
  3650  			"fieldsV1": {"f:spec":{"f:claimRef":{}}},
  3651  			"manager": "kube-controller-manager",
  3652  			"operation": "Update",
  3653  			"time": "2022-06-08T23:46:37Z"
  3654  		}
  3655  	]`)
  3656  
  3657  	err = json.Unmarshal(expectedManagedFieldsString, &expectedManagedFields)
  3658  	if err != nil {
  3659  		t.Fatalf("unexpectly failed to decode expected managed fields")
  3660  	}
  3661  
  3662  	// Wipe timestamps before comparison
  3663  	for i := range expectedManagedFields {
  3664  		expectedManagedFields[i].Time = nil
  3665  	}
  3666  
  3667  	for i := range managedFields {
  3668  		managedFields[i].Time = nil
  3669  	}
  3670  
  3671  	if !reflect.DeepEqual(expectedManagedFields, managedFields) {
  3672  		t.Fatalf("unexpected managed fields: %v", cmp.Diff(expectedManagedFields, managedFields))
  3673  	}
  3674  }
  3675  
  3676  func TestDuplicatesInAssociativeLists(t *testing.T) {
  3677  	client, closeFn := setup(t)
  3678  	defer closeFn()
  3679  
  3680  	ds := []byte(`{
  3681    "apiVersion": "apps/v1",
  3682    "kind": "DaemonSet",
  3683    "metadata": {
  3684      "name": "example-daemonset",
  3685      "labels": {
  3686        "app": "example"
  3687      }
  3688    },
  3689    "spec": {
  3690      "selector": {
  3691        "matchLabels": {
  3692          "app": "example"
  3693        }
  3694      },
  3695      "template": {
  3696        "metadata": {
  3697          "labels": {
  3698            "app": "example"
  3699          }
  3700        },
  3701        "spec": {
  3702          "containers": [
  3703            {
  3704              "name": "nginx",
  3705              "image": "nginx",
  3706              "ports": [
  3707                {
  3708                  "name": "port0",
  3709                  "containerPort": 1
  3710                },
  3711                {
  3712                	"name": "port1",
  3713                  "containerPort": 80
  3714                },
  3715                {
  3716                	"name": "port2",
  3717                  "containerPort": 80
  3718                }
  3719              ],
  3720              "env": [
  3721                {
  3722                  "name": "ENV0",
  3723                  "value": "/env0value"
  3724                },
  3725                {
  3726                  "name": "PATH",
  3727                  "value": "/bin"
  3728                },
  3729                {
  3730                  "name": "PATH",
  3731                  "value": "$PATH:/usr/bin"
  3732                }
  3733              ]
  3734            }
  3735          ]
  3736        }
  3737      }
  3738    }
  3739  }`)
  3740  	// Create the object
  3741  	obj, err := client.AppsV1().RESTClient().
  3742  		Post().
  3743  		Namespace("default").
  3744  		Param("fieldManager", "create").
  3745  		Resource("daemonsets").
  3746  		Body(ds).
  3747  		Do(context.TODO()).
  3748  		Get()
  3749  	if err != nil {
  3750  		t.Fatalf("Failed to create the object: %v", err)
  3751  	}
  3752  	daemon := obj.(*appsv1.DaemonSet)
  3753  	if want, got := 3, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
  3754  		t.Fatalf("Expected %v EnvVars, got %v", want, got)
  3755  	}
  3756  	if want, got := 3, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
  3757  		t.Fatalf("Expected %v Ports, got %v", want, got)
  3758  	}
  3759  
  3760  	expectManagedFields(t, daemon.ManagedFields, `
  3761  [
  3762  	{
  3763  		"manager": "create",
  3764  		"operation": "Update",
  3765  		"apiVersion": "apps/v1",
  3766  		"time": null,
  3767  		"fieldsType": "FieldsV1",
  3768  		"fieldsV1": {
  3769  			"f:metadata": {
  3770  				"f:annotations": {
  3771  					".": {},
  3772  					"f:deprecated.daemonset.template.generation": {}
  3773  				},
  3774  				"f:labels": {
  3775  					".": {},
  3776  					"f:app": {}
  3777  				}
  3778  			},
  3779  			"f:spec": {
  3780  			"f:revisionHistoryLimit": {},
  3781  			"f:selector": {},
  3782  			"f:template": {
  3783  				"f:metadata": {
  3784  					"f:labels": {
  3785  						".": {},
  3786  						"f:app": {}
  3787  					}
  3788  				},
  3789  				"f:spec": {
  3790  					"f:containers": {
  3791  						"k:{\"name\":\"nginx\"}": {
  3792  						".": {},
  3793  						"f:env": {
  3794  							".": {},
  3795  							"k:{\"name\":\"ENV0\"}": {
  3796  								".": {},
  3797  								"f:name": {},
  3798  								"f:value": {}
  3799  							},
  3800  							"k:{\"name\":\"PATH\"}": {}
  3801  						},
  3802  						"f:image": {},
  3803  						"f:imagePullPolicy": {},
  3804  						"f:name": {},
  3805  						"f:ports": {
  3806  							".": {},
  3807  							"k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
  3808  								".": {},
  3809  								"f:containerPort": {},
  3810  								"f:name": {},
  3811  								"f:protocol": {}
  3812  							},
  3813  							"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
  3814  						},
  3815  						"f:resources": {},
  3816  						"f:terminationMessagePath": {},
  3817  						"f:terminationMessagePolicy": {}
  3818  						}
  3819  					},
  3820  					"f:dnsPolicy": {},
  3821  					"f:restartPolicy": {},
  3822  					"f:schedulerName": {},
  3823  					"f:securityContext": {},
  3824  					"f:terminationGracePeriodSeconds": {}
  3825  					}
  3826  				},
  3827  				"f:updateStrategy": {
  3828  					"f:rollingUpdate": {
  3829  					".": {},
  3830  					"f:maxSurge": {},
  3831  					"f:maxUnavailable": {}
  3832  					},
  3833  					"f:type": {}
  3834  				}
  3835  			}
  3836  		}
  3837  	}
  3838  ]`)
  3839  
  3840  	// Apply unrelated fields, fieldmanager should be strictly additive.
  3841  	ds = []byte(`
  3842  apiVersion: apps/v1
  3843  kind: DaemonSet
  3844  metadata:
  3845    name: example-daemonset
  3846    labels:
  3847      app: example
  3848  spec:
  3849    selector:
  3850      matchLabels:
  3851        app: example
  3852    template:
  3853      spec:
  3854        containers:
  3855          - name: nginx
  3856            image: nginx
  3857            ports:
  3858              - name: port3
  3859                containerPort: 443
  3860            env:
  3861              - name: HOME
  3862                value: "/usr/home"
  3863  `)
  3864  	obj, err = client.AppsV1().RESTClient().
  3865  		Patch(types.ApplyPatchType).
  3866  		Namespace("default").
  3867  		Name("example-daemonset").
  3868  		Param("fieldManager", "apply").
  3869  		Resource("daemonsets").
  3870  		Body(ds).
  3871  		Do(context.TODO()).
  3872  		Get()
  3873  	if err != nil {
  3874  		t.Fatalf("Failed to aply the first object: %v", err)
  3875  	}
  3876  	daemon = obj.(*appsv1.DaemonSet)
  3877  	if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
  3878  		t.Fatalf("Expected %v EnvVars, got %v", want, got)
  3879  	}
  3880  	if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
  3881  		t.Fatalf("Expected %v Ports, got %v", want, got)
  3882  	}
  3883  
  3884  	expectManagedFields(t, daemon.ManagedFields, `
  3885  [
  3886  	{
  3887  		"manager": "apply",
  3888  		"operation": "Apply",
  3889  		"apiVersion": "apps/v1",
  3890  		"time": null,
  3891  		"fieldsType": "FieldsV1",
  3892  		"fieldsV1": {
  3893  			"f:metadata": {
  3894  				"f:labels": {
  3895  					"f:app": {}
  3896  				}
  3897  			},
  3898  			"f:spec": {
  3899  				"f:selector": {},
  3900  				"f:template": {
  3901  					"f:spec": {
  3902  						"f:containers": {
  3903  							"k:{\"name\":\"nginx\"}": {
  3904  								".": {},
  3905  								"f:env": {
  3906  									"k:{\"name\":\"HOME\"}": {
  3907  										".": {},
  3908  										"f:name": {},
  3909  										"f:value": {}
  3910  									}
  3911  								},
  3912  								"f:image": {},
  3913  								"f:name": {},
  3914  								"f:ports": {
  3915  									"k:{\"containerPort\":443,\"protocol\":\"TCP\"}": {
  3916  										".": {},
  3917  										"f:containerPort": {},
  3918  										"f:name": {}
  3919  									}
  3920  								}
  3921  							}
  3922  						}
  3923  					}
  3924  				}
  3925  			}
  3926  		}
  3927  	},
  3928  	{
  3929  		"manager": "create",
  3930  		"operation": "Update",
  3931  		"apiVersion": "apps/v1",
  3932  		"time": null,
  3933  		"fieldsType": "FieldsV1",
  3934  		"fieldsV1": {
  3935  			"f:metadata": {
  3936  				"f:annotations": {
  3937  					".": {},
  3938  					"f:deprecated.daemonset.template.generation": {}
  3939  				},
  3940  				"f:labels": {
  3941  					".": {},
  3942  					"f:app": {}
  3943  				}
  3944  			},
  3945  			"f:spec": {
  3946  				"f:revisionHistoryLimit": {},
  3947  				"f:selector": {},
  3948  				"f:template": {
  3949  					"f:metadata": {
  3950  						"f:labels": {
  3951  							".": {},
  3952  							"f:app": {}
  3953  						}
  3954  					},
  3955  					"f:spec": {
  3956  						"f:containers": {
  3957  							"k:{\"name\":\"nginx\"}": {
  3958  								".": {},
  3959  								"f:env": {
  3960  									".": {},
  3961  									"k:{\"name\":\"ENV0\"}": {
  3962  										".": {},
  3963  										"f:name": {},
  3964  										"f:value": {}
  3965  									},
  3966  									"k:{\"name\":\"PATH\"}": {}
  3967  								},
  3968  								"f:image": {},
  3969  								"f:imagePullPolicy": {},
  3970  								"f:name": {},
  3971  								"f:ports": {
  3972  									".": {},
  3973  									"k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
  3974  										".": {},
  3975  										"f:containerPort": {},
  3976  										"f:name": {},
  3977  										"f:protocol": {}
  3978  									},
  3979  									"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
  3980  								},
  3981  								"f:resources": {},
  3982  								"f:terminationMessagePath": {},
  3983  								"f:terminationMessagePolicy": {}
  3984  							}
  3985  						},
  3986  						"f:dnsPolicy": {},
  3987  						"f:restartPolicy": {},
  3988  						"f:schedulerName": {},
  3989  						"f:securityContext": {},
  3990  						"f:terminationGracePeriodSeconds": {}
  3991  					}
  3992  				},
  3993  				"f:updateStrategy": {
  3994  					"f:rollingUpdate": {
  3995  						".": {},
  3996  						"f:maxSurge": {},
  3997  						"f:maxUnavailable": {}
  3998  					},
  3999  					"f:type": {}
  4000  				}
  4001  			}
  4002  		}
  4003  	}
  4004  ]
  4005  `)
  4006  
  4007  	// Change name of some ports.
  4008  	ds = []byte(`{
  4009    "apiVersion": "apps/v1",
  4010    "kind": "DaemonSet",
  4011    "metadata": {
  4012      "name": "example-daemonset",
  4013      "labels": {
  4014        "app": "example"
  4015      }
  4016    },
  4017    "spec": {
  4018      "selector": {
  4019        "matchLabels": {
  4020          "app": "example"
  4021        }
  4022      },
  4023      "template": {
  4024        "metadata": {
  4025          "labels": {
  4026            "app": "example"
  4027          }
  4028        },
  4029        "spec": {
  4030          "containers": [
  4031            {
  4032              "name": "nginx",
  4033              "image": "nginx",
  4034              "ports": [
  4035                {
  4036                  "name": "port0",
  4037                  "containerPort": 1
  4038                },
  4039                {
  4040                	"name": "port3",
  4041                  "containerPort": 443
  4042                },
  4043                {
  4044                	"name": "port4",
  4045                  "containerPort": 80
  4046                },
  4047                {
  4048                	"name": "port5",
  4049                  "containerPort": 80
  4050                }
  4051              ],
  4052              "env": [
  4053                {
  4054                  "name": "ENV0",
  4055                  "value": "/env0value"
  4056                },
  4057                {
  4058                  "name": "PATH",
  4059                  "value": "/bin"
  4060                },
  4061                {
  4062                  "name": "PATH",
  4063                  "value": "$PATH:/usr/bin:/usr/local/bin"
  4064                },
  4065                {
  4066                  "name": "HOME",
  4067                  "value": "/usr/home"
  4068                }
  4069              ]
  4070            }
  4071          ]
  4072        }
  4073      }
  4074    }
  4075  }`)
  4076  	obj, err = client.AppsV1().RESTClient().
  4077  		Put().
  4078  		Namespace("default").
  4079  		Name("example-daemonset").
  4080  		Param("fieldManager", "update").
  4081  		Resource("daemonsets").
  4082  		Body(ds).
  4083  		Do(context.TODO()).
  4084  		Get()
  4085  	if err != nil {
  4086  		t.Fatalf("Failed to update the object: %v", err)
  4087  	}
  4088  	daemon = obj.(*appsv1.DaemonSet)
  4089  	if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
  4090  		t.Fatalf("Expected %v EnvVars, got %v", want, got)
  4091  	}
  4092  	if want, got := 4, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
  4093  		t.Fatalf("Expected %v Ports, got %v", want, got)
  4094  	}
  4095  
  4096  	expectManagedFields(t, daemon.ManagedFields, `
  4097  [
  4098  	{
  4099  		"manager": "apply",
  4100  		"operation": "Apply",
  4101  		"apiVersion": "apps/v1",
  4102  		"time": null,
  4103  		"fieldsType": "FieldsV1",
  4104  		"fieldsV1": {
  4105  			"f:metadata": {
  4106  				"f:labels": {
  4107  					"f:app": {}
  4108  				}
  4109  			},
  4110  			"f:spec": {
  4111  				"f:selector": {},
  4112  				"f:template": {
  4113  					"f:spec": {
  4114  						"f:containers": {
  4115  							"k:{\"name\":\"nginx\"}": {
  4116  								".": {},
  4117  								"f:env": {
  4118  									"k:{\"name\":\"HOME\"}": {
  4119  										".": {},
  4120  										"f:name": {},
  4121  										"f:value": {}
  4122  									}
  4123  								},
  4124  								"f:image": {},
  4125  								"f:name": {},
  4126  								"f:ports": {
  4127  									"k:{\"containerPort\":443,\"protocol\":\"TCP\"}": {
  4128  										".": {},
  4129  										"f:containerPort": {},
  4130  										"f:name": {}
  4131  									}
  4132  								}
  4133  							}
  4134  						}
  4135  					}
  4136  				}
  4137  			}
  4138  		}
  4139  	},
  4140  	{
  4141  		"manager": "create",
  4142  		"operation": "Update",
  4143  		"apiVersion": "apps/v1",
  4144  		"time": null,
  4145  		"fieldsType": "FieldsV1",
  4146  		"fieldsV1": {
  4147  			"f:metadata": {
  4148  				"f:annotations": {},
  4149  				"f:labels": {
  4150  					".": {},
  4151  					"f:app": {}
  4152  				}
  4153  			},
  4154  			"f:spec": {
  4155  				"f:revisionHistoryLimit": {},
  4156  				"f:selector": {},
  4157  				"f:template": {
  4158  					"f:metadata": {
  4159  						"f:labels": {
  4160  							".": {},
  4161  							"f:app": {}
  4162  						}
  4163  					},
  4164  					"f:spec": {
  4165  						"f:containers": {
  4166  							"k:{\"name\":\"nginx\"}": {
  4167  								".": {},
  4168  								"f:env": {
  4169  									".": {},
  4170  									"k:{\"name\":\"ENV0\"}": {
  4171  										".": {},
  4172  										"f:name": {},
  4173  										"f:value": {}
  4174  									}
  4175  								},
  4176  								"f:image": {},
  4177  								"f:imagePullPolicy": {},
  4178  								"f:name": {},
  4179  								"f:ports": {
  4180  									".": {},
  4181  									"k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
  4182  										".": {},
  4183  										"f:containerPort": {},
  4184  										"f:name": {},
  4185  										"f:protocol": {}
  4186  									}
  4187  								},
  4188  								"f:resources": {},
  4189  								"f:terminationMessagePath": {},
  4190  								"f:terminationMessagePolicy": {}
  4191  							}
  4192  						},
  4193  						"f:dnsPolicy": {},
  4194  						"f:restartPolicy": {},
  4195  						"f:schedulerName": {},
  4196  						"f:securityContext": {},
  4197  						"f:terminationGracePeriodSeconds": {}
  4198  					}
  4199  				},
  4200  				"f:updateStrategy": {
  4201  					"f:rollingUpdate": {
  4202  						".": {},
  4203  						"f:maxSurge": {},
  4204  						"f:maxUnavailable": {}
  4205  					},
  4206  					"f:type": {}
  4207  				}
  4208  			}
  4209  		}
  4210  	},
  4211  	{
  4212  		"manager": "update",
  4213  		"operation": "Update",
  4214  		"apiVersion": "apps/v1",
  4215  		"time": null,
  4216  		"fieldsType": "FieldsV1",
  4217  		"fieldsV1": {
  4218  			"f:metadata": {
  4219  				"f:annotations": {
  4220  				"f:deprecated.daemonset.template.generation": {}
  4221  				}
  4222  			},
  4223  			"f:spec": {
  4224  				"f:template": {
  4225  					"f:spec": {
  4226  						"f:containers": {
  4227  							"k:{\"name\":\"nginx\"}": {
  4228  								"f:env": {
  4229  									"k:{\"name\":\"PATH\"}": {}
  4230  								},
  4231  								"f:ports": {
  4232  									"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {}
  4233  								}
  4234  							}
  4235  						}
  4236  					}
  4237  				}
  4238  			}
  4239  		}
  4240  	}
  4241  ]
  4242  `)
  4243  
  4244  	// Replaces envvars and paths.
  4245  	ds = []byte(`
  4246  apiVersion: apps/v1
  4247  kind: DaemonSet
  4248  metadata:
  4249    name: example-daemonset
  4250    labels:
  4251      app: example
  4252  spec:
  4253    selector:
  4254      matchLabels:
  4255        app: example
  4256    template:
  4257      spec:
  4258        containers:
  4259          - name: nginx
  4260            image: nginx
  4261            ports:
  4262              - name: port80
  4263                containerPort: 80
  4264            env:
  4265              - name: PATH
  4266                value: "/bin:/usr/bin:/usr/local/bin"
  4267  `)
  4268  	obj, err = client.AppsV1().RESTClient().
  4269  		Patch(types.ApplyPatchType).
  4270  		Namespace("default").
  4271  		Name("example-daemonset").
  4272  		Param("fieldManager", "apply").
  4273  		Param("force", "true").
  4274  		Resource("daemonsets").
  4275  		Body(ds).
  4276  		Do(context.TODO()).
  4277  		Get()
  4278  	if err != nil {
  4279  		t.Fatalf("Failed to apply the second object: %v", err)
  4280  	}
  4281  
  4282  	daemon = obj.(*appsv1.DaemonSet)
  4283  	// HOME is removed, PATH is replaced with 1.
  4284  	if want, got := 2, len(daemon.Spec.Template.Spec.Containers[0].Env); want != got {
  4285  		t.Fatalf("Expected %v EnvVars, got %v", want, got)
  4286  	}
  4287  	if want, got := 2, len(daemon.Spec.Template.Spec.Containers[0].Ports); want != got {
  4288  		t.Fatalf("Expected %v Ports, got %v", want, got)
  4289  	}
  4290  
  4291  	expectManagedFields(t, daemon.ManagedFields, `
  4292  [
  4293  	{
  4294  		"manager": "apply",
  4295  		"operation": "Apply",
  4296  		"apiVersion": "apps/v1",
  4297  		"time": null,
  4298  		"fieldsType": "FieldsV1",
  4299  		"fieldsV1": {
  4300  			"f:metadata": {
  4301  				"f:labels": {
  4302  					"f:app": {}
  4303  				}
  4304  			},
  4305  			"f:spec": {
  4306  				"f:selector": {},
  4307  				"f:template": {
  4308  					"f:spec": {
  4309  						"f:containers": {
  4310  							"k:{\"name\":\"nginx\"}": {
  4311  								".": {},
  4312  								"f:env": {
  4313  									"k:{\"name\":\"PATH\"}": {
  4314  										".": {},
  4315  										"f:name": {},
  4316  										"f:value": {}
  4317  									}
  4318  								},
  4319  								"f:image": {},
  4320  								"f:name": {},
  4321  								"f:ports": {
  4322  									"k:{\"containerPort\":80,\"protocol\":\"TCP\"}": {
  4323  										".": {},
  4324  										"f:containerPort": {},
  4325  										"f:name": {}
  4326  									}
  4327  								}
  4328  							}
  4329  						}
  4330  					}
  4331  				}
  4332  			}
  4333  		}
  4334  	},
  4335  	{
  4336  		"manager": "create",
  4337  		"operation": "Update",
  4338  		"apiVersion": "apps/v1",
  4339  		"time": null,
  4340  		"fieldsType": "FieldsV1",
  4341  		"fieldsV1": {
  4342  			"f:metadata": {
  4343  				"f:annotations": {},
  4344  				"f:labels": {
  4345  					".": {},
  4346  					"f:app": {}
  4347  				}
  4348  			},
  4349  			"f:spec": {
  4350  				"f:revisionHistoryLimit": {},
  4351  				"f:selector": {},
  4352  				"f:template": {
  4353  					"f:metadata": {
  4354  						"f:labels": {
  4355  							".": {},
  4356  							"f:app": {}
  4357  						}
  4358  					},
  4359  					"f:spec": {
  4360  						"f:containers": {
  4361  							"k:{\"name\":\"nginx\"}": {
  4362  								".": {},
  4363  								"f:env": {
  4364  									".": {},
  4365  									"k:{\"name\":\"ENV0\"}": {
  4366  										".": {},
  4367  										"f:name": {},
  4368  										"f:value": {}
  4369  									}
  4370  								},
  4371  								"f:image": {},
  4372  								"f:imagePullPolicy": {},
  4373  								"f:name": {},
  4374  								"f:ports": {
  4375  									".": {},
  4376  									"k:{\"containerPort\":1,\"protocol\":\"TCP\"}": {
  4377  										".": {},
  4378  										"f:containerPort": {},
  4379  										"f:name": {},
  4380  										"f:protocol": {}
  4381  									}
  4382  								},
  4383  								"f:resources": {},
  4384  								"f:terminationMessagePath": {},
  4385  								"f:terminationMessagePolicy": {}
  4386  							}
  4387  						},
  4388  						"f:dnsPolicy": {},
  4389  						"f:restartPolicy": {},
  4390  						"f:schedulerName": {},
  4391  						"f:securityContext": {},
  4392  						"f:terminationGracePeriodSeconds": {}
  4393  					}
  4394  				},
  4395  				"f:updateStrategy": {
  4396  					"f:rollingUpdate": {
  4397  						".": {},
  4398  						"f:maxSurge": {},
  4399  						"f:maxUnavailable": {}
  4400  					},
  4401  					"f:type": {}
  4402  				}
  4403  			}
  4404  		}
  4405  	},
  4406  	{
  4407  		"manager": "update",
  4408  		"operation": "Update",
  4409  		"apiVersion": "apps/v1",
  4410  		"time": null,
  4411  		"fieldsType": "FieldsV1",
  4412  		"fieldsV1": {
  4413  			"f:metadata": {
  4414  				"f:annotations": {
  4415  					"f:deprecated.daemonset.template.generation": {}
  4416  				}
  4417  			}
  4418  		}
  4419  	}
  4420  ]
  4421  `)
  4422  }
  4423  
  4424  func expectManagedFields(t *testing.T, managedFields []metav1.ManagedFieldsEntry, expect string) {
  4425  	t.Helper()
  4426  	for i := range managedFields {
  4427  		managedFields[i].Time = &metav1.Time{}
  4428  	}
  4429  	got, err := json.MarshalIndent(managedFields, "", "  ")
  4430  	if err != nil {
  4431  		t.Fatalf("Failed to marshal managed fields: %v", err)
  4432  	}
  4433  	b := &bytes.Buffer{}
  4434  	err = json.Indent(b, []byte(expect), "", "  ")
  4435  	if err != nil {
  4436  		t.Fatalf("Failed to indent json: %v", err)
  4437  	}
  4438  	want := b.String()
  4439  	diff := cmp.Diff(strings.Split(strings.TrimSpace(string(got)), "\n"), strings.Split(strings.TrimSpace(want), "\n"))
  4440  	if len(diff) > 0 {
  4441  		t.Fatalf("Want:\n%s\nGot:\n%s\nDiff:\n%s", string(want), string(got), diff)
  4442  	}
  4443  }
  4444  

View as plain text