...

Source file src/k8s.io/kubernetes/test/integration/client/client_test.go

Documentation: k8s.io/kubernetes/test/integration/client

     1  /*
     2  Copyright 2014 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 client
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"log"
    23  	"reflect"
    24  	rt "runtime"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/google/go-cmp/cmp"
    31  	appsv1 "k8s.io/api/apps/v1"
    32  	v1 "k8s.io/api/core/v1"
    33  	eventsv1 "k8s.io/api/events/v1"
    34  	"k8s.io/apimachinery/pkg/api/equality"
    35  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    36  	"k8s.io/apimachinery/pkg/api/resource"
    37  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    38  	"k8s.io/apimachinery/pkg/fields"
    39  	"k8s.io/apimachinery/pkg/labels"
    40  	"k8s.io/apimachinery/pkg/runtime"
    41  	"k8s.io/apimachinery/pkg/runtime/schema"
    42  	"k8s.io/apimachinery/pkg/types"
    43  	"k8s.io/apimachinery/pkg/util/wait"
    44  	"k8s.io/apimachinery/pkg/watch"
    45  	appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
    46  	corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
    47  	metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
    48  	clientset "k8s.io/client-go/kubernetes"
    49  	"k8s.io/utils/pointer"
    50  
    51  	"k8s.io/component-base/version"
    52  	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
    53  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    54  	"k8s.io/kubernetes/test/integration/framework"
    55  	imageutils "k8s.io/kubernetes/test/utils/image"
    56  )
    57  
    58  func TestClient(t *testing.T) {
    59  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
    60  	defer result.TearDownFn()
    61  
    62  	client := clientset.NewForConfigOrDie(result.ClientConfig)
    63  
    64  	info, err := client.Discovery().ServerVersion()
    65  	if err != nil {
    66  		t.Fatalf("unexpected error: %v", err)
    67  	}
    68  	if e, a := version.Get(), *info; !reflect.DeepEqual(e, a) {
    69  		t.Errorf("expected %#v, got %#v", e, a)
    70  	}
    71  
    72  	pods, err := client.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
    73  	if err != nil {
    74  		t.Fatalf("unexpected error: %v", err)
    75  	}
    76  	if len(pods.Items) != 0 {
    77  		t.Errorf("expected no pods, got %#v", pods)
    78  	}
    79  
    80  	// get a validation error
    81  	pod := &v1.Pod{
    82  		ObjectMeta: metav1.ObjectMeta{
    83  			GenerateName: "test",
    84  			Namespace:    "default",
    85  		},
    86  		Spec: v1.PodSpec{
    87  			Containers: []v1.Container{
    88  				{
    89  					Name: "test",
    90  				},
    91  			},
    92  		},
    93  	}
    94  
    95  	got, err := client.CoreV1().Pods("default").Create(context.TODO(), pod, metav1.CreateOptions{})
    96  	if err == nil {
    97  		t.Fatalf("unexpected non-error: %v", got)
    98  	}
    99  
   100  	// get a created pod
   101  	pod.Spec.Containers[0].Image = "an-image"
   102  	got, err = client.CoreV1().Pods("default").Create(context.TODO(), pod, metav1.CreateOptions{})
   103  	if err != nil {
   104  		t.Fatalf("unexpected error: %v", err)
   105  	}
   106  	if got.Name == "" {
   107  		t.Errorf("unexpected empty pod Name %v", got)
   108  	}
   109  
   110  	// pod is shown, but not scheduled
   111  	pods, err = client.CoreV1().Pods("default").List(context.TODO(), metav1.ListOptions{})
   112  	if err != nil {
   113  		t.Fatalf("unexpected error: %v", err)
   114  	}
   115  	if len(pods.Items) != 1 {
   116  		t.Errorf("expected one pod, got %#v", pods)
   117  	}
   118  	actual := pods.Items[0]
   119  	if actual.Name != got.Name {
   120  		t.Errorf("expected pod %#v, got %#v", got, actual)
   121  	}
   122  	if actual.Spec.NodeName != "" {
   123  		t.Errorf("expected pod to be unscheduled, got %#v", actual)
   124  	}
   125  }
   126  
   127  func TestAtomicPut(t *testing.T) {
   128  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
   129  	defer result.TearDownFn()
   130  
   131  	c := clientset.NewForConfigOrDie(result.ClientConfig)
   132  
   133  	rcBody := v1.ReplicationController{
   134  		TypeMeta: metav1.TypeMeta{
   135  			APIVersion: c.CoreV1().RESTClient().APIVersion().String(),
   136  		},
   137  		ObjectMeta: metav1.ObjectMeta{
   138  			Name:      "atomicrc",
   139  			Namespace: "default",
   140  			Labels: map[string]string{
   141  				"name": "atomicrc",
   142  			},
   143  		},
   144  		Spec: v1.ReplicationControllerSpec{
   145  			Replicas: pointer.Int32(0),
   146  			Selector: map[string]string{
   147  				"foo": "bar",
   148  			},
   149  			Template: &v1.PodTemplateSpec{
   150  				ObjectMeta: metav1.ObjectMeta{
   151  					Labels: map[string]string{
   152  						"foo": "bar",
   153  					},
   154  				},
   155  				Spec: v1.PodSpec{
   156  					Containers: []v1.Container{
   157  						{Name: "name", Image: "image"},
   158  					},
   159  				},
   160  			},
   161  		},
   162  	}
   163  	rcs := c.CoreV1().ReplicationControllers("default")
   164  	rc, err := rcs.Create(context.TODO(), &rcBody, metav1.CreateOptions{})
   165  	if err != nil {
   166  		t.Fatalf("Failed creating atomicRC: %v", err)
   167  	}
   168  	testLabels := labels.Set{
   169  		"foo": "bar",
   170  	}
   171  	for i := 0; i < 5; i++ {
   172  		// a: z, b: y, etc...
   173  		testLabels[string([]byte{byte('a' + i)})] = string([]byte{byte('z' - i)})
   174  	}
   175  	var wg sync.WaitGroup
   176  	wg.Add(len(testLabels))
   177  	for label, value := range testLabels {
   178  		go func(l, v string) {
   179  			defer wg.Done()
   180  			for {
   181  				tmpRC, err := rcs.Get(context.TODO(), rc.Name, metav1.GetOptions{})
   182  				if err != nil {
   183  					t.Errorf("Error getting atomicRC: %v", err)
   184  					continue
   185  				}
   186  				if tmpRC.Spec.Selector == nil {
   187  					tmpRC.Spec.Selector = map[string]string{l: v}
   188  					tmpRC.Spec.Template.Labels = map[string]string{l: v}
   189  				} else {
   190  					tmpRC.Spec.Selector[l] = v
   191  					tmpRC.Spec.Template.Labels[l] = v
   192  				}
   193  				_, err = rcs.Update(context.TODO(), tmpRC, metav1.UpdateOptions{})
   194  				if err != nil {
   195  					if apierrors.IsConflict(err) {
   196  						// This is what we expect.
   197  						continue
   198  					}
   199  					t.Errorf("Unexpected error putting atomicRC: %v", err)
   200  					continue
   201  				}
   202  				return
   203  			}
   204  		}(label, value)
   205  	}
   206  	wg.Wait()
   207  	rc, err = rcs.Get(context.TODO(), rc.Name, metav1.GetOptions{})
   208  	if err != nil {
   209  		t.Fatalf("Failed getting atomicRC after writers are complete: %v", err)
   210  	}
   211  	if !reflect.DeepEqual(testLabels, labels.Set(rc.Spec.Selector)) {
   212  		t.Errorf("Selector PUTs were not atomic: wanted %v, got %v", testLabels, rc.Spec.Selector)
   213  	}
   214  }
   215  
   216  func TestPatch(t *testing.T) {
   217  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
   218  	defer result.TearDownFn()
   219  
   220  	c := clientset.NewForConfigOrDie(result.ClientConfig)
   221  
   222  	name := "patchpod"
   223  	resource := "pods"
   224  	podBody := v1.Pod{
   225  		TypeMeta: metav1.TypeMeta{
   226  			APIVersion: c.CoreV1().RESTClient().APIVersion().String(),
   227  		},
   228  		ObjectMeta: metav1.ObjectMeta{
   229  			Name:      name,
   230  			Namespace: "default",
   231  			Labels:    map[string]string{},
   232  		},
   233  		Spec: v1.PodSpec{
   234  			Containers: []v1.Container{
   235  				{Name: "name", Image: "image"},
   236  			},
   237  		},
   238  	}
   239  	pods := c.CoreV1().Pods("default")
   240  	_, err := pods.Create(context.TODO(), &podBody, metav1.CreateOptions{})
   241  	if err != nil {
   242  		t.Fatalf("Failed creating patchpods: %v", err)
   243  	}
   244  
   245  	patchBodies := map[schema.GroupVersion]map[types.PatchType]struct {
   246  		AddLabelBody        []byte
   247  		RemoveLabelBody     []byte
   248  		RemoveAllLabelsBody []byte
   249  	}{
   250  		v1.SchemeGroupVersion: {
   251  			types.JSONPatchType: {
   252  				[]byte(`[{"op":"add","path":"/metadata/labels","value":{"foo":"bar","baz":"qux"}}]`),
   253  				[]byte(`[{"op":"remove","path":"/metadata/labels/foo"}]`),
   254  				[]byte(`[{"op":"remove","path":"/metadata/labels"}]`),
   255  			},
   256  			types.MergePatchType: {
   257  				[]byte(`{"metadata":{"labels":{"foo":"bar","baz":"qux"}}}`),
   258  				[]byte(`{"metadata":{"labels":{"foo":null}}}`),
   259  				[]byte(`{"metadata":{"labels":null}}`),
   260  			},
   261  			types.StrategicMergePatchType: {
   262  				[]byte(`{"metadata":{"labels":{"foo":"bar","baz":"qux"}}}`),
   263  				[]byte(`{"metadata":{"labels":{"foo":null}}}`),
   264  				[]byte(`{"metadata":{"labels":{"$patch":"replace"}}}`),
   265  			},
   266  		},
   267  	}
   268  
   269  	pb := patchBodies[c.CoreV1().RESTClient().APIVersion()]
   270  
   271  	execPatch := func(pt types.PatchType, body []byte) error {
   272  		result := c.CoreV1().RESTClient().Patch(pt).
   273  			Resource(resource).
   274  			Namespace("default").
   275  			Name(name).
   276  			Body(body).
   277  			Do(context.TODO())
   278  		if result.Error() != nil {
   279  			return result.Error()
   280  		}
   281  
   282  		// trying to chase flakes, this should give us resource versions of objects as we step through
   283  		jsonObj, err := result.Raw()
   284  		if err != nil {
   285  			t.Log(err)
   286  		} else {
   287  			t.Logf("%v", string(jsonObj))
   288  		}
   289  
   290  		return nil
   291  	}
   292  
   293  	for k, v := range pb {
   294  		// add label
   295  		err := execPatch(k, v.AddLabelBody)
   296  		if err != nil {
   297  			t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
   298  		}
   299  		pod, err := pods.Get(context.TODO(), name, metav1.GetOptions{})
   300  		if err != nil {
   301  			t.Fatalf("Failed getting patchpod: %v", err)
   302  		}
   303  		if len(pod.Labels) != 2 || pod.Labels["foo"] != "bar" || pod.Labels["baz"] != "qux" {
   304  			t.Errorf("Failed updating patchpod with patch type %s: labels are: %v", k, pod.Labels)
   305  		}
   306  
   307  		// remove one label
   308  		err = execPatch(k, v.RemoveLabelBody)
   309  		if err != nil {
   310  			t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
   311  		}
   312  		pod, err = pods.Get(context.TODO(), name, metav1.GetOptions{})
   313  		if err != nil {
   314  			t.Fatalf("Failed getting patchpod: %v", err)
   315  		}
   316  		if len(pod.Labels) != 1 || pod.Labels["baz"] != "qux" {
   317  			t.Errorf("Failed updating patchpod with patch type %s: labels are: %v", k, pod.Labels)
   318  		}
   319  
   320  		// remove all labels
   321  		err = execPatch(k, v.RemoveAllLabelsBody)
   322  		if err != nil {
   323  			t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
   324  		}
   325  		pod, err = pods.Get(context.TODO(), name, metav1.GetOptions{})
   326  		if err != nil {
   327  			t.Fatalf("Failed getting patchpod: %v", err)
   328  		}
   329  		if pod.Labels != nil {
   330  			t.Errorf("Failed remove all labels from patchpod with patch type %s: %v", k, pod.Labels)
   331  		}
   332  	}
   333  }
   334  
   335  func TestPatchWithCreateOnUpdate(t *testing.T) {
   336  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   337  	defer result.TearDownFn()
   338  
   339  	c := clientset.NewForConfigOrDie(result.ClientConfig)
   340  
   341  	endpointTemplate := &v1.Endpoints{
   342  		ObjectMeta: metav1.ObjectMeta{
   343  			Name:      "patchendpoint",
   344  			Namespace: "default",
   345  		},
   346  		Subsets: []v1.EndpointSubset{
   347  			{
   348  				Addresses: []v1.EndpointAddress{{IP: "1.2.3.4"}},
   349  				Ports:     []v1.EndpointPort{{Port: 80, Protocol: v1.ProtocolTCP}},
   350  			},
   351  		},
   352  	}
   353  
   354  	patchEndpoint := func(json []byte) (runtime.Object, error) {
   355  		return c.CoreV1().RESTClient().Patch(types.MergePatchType).Resource("endpoints").Namespace("default").Name("patchendpoint").Body(json).Do(context.TODO()).Get()
   356  	}
   357  
   358  	// Make sure patch doesn't get to CreateOnUpdate
   359  	{
   360  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
   361  		if err != nil {
   362  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   363  		}
   364  		if obj, err := patchEndpoint(endpointJSON); !apierrors.IsNotFound(err) {
   365  			t.Errorf("Expected notfound creating from patch, got error=%v and object: %#v", err, obj)
   366  		}
   367  	}
   368  
   369  	// Create the endpoint (endpoints set AllowCreateOnUpdate=true) to get a UID and resource version
   370  	createdEndpoint, err := c.CoreV1().Endpoints("default").Update(context.TODO(), endpointTemplate, metav1.UpdateOptions{})
   371  	if err != nil {
   372  		t.Fatalf("Failed creating endpoint: %v", err)
   373  	}
   374  
   375  	// Make sure identity patch is accepted
   376  	{
   377  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), createdEndpoint)
   378  		if err != nil {
   379  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   380  		}
   381  		if _, err := patchEndpoint(endpointJSON); err != nil {
   382  			t.Errorf("Failed patching endpoint: %v", err)
   383  		}
   384  	}
   385  
   386  	// Make sure patch complains about a mismatched resourceVersion
   387  	{
   388  		endpointTemplate.Name = ""
   389  		endpointTemplate.UID = ""
   390  		endpointTemplate.ResourceVersion = "1"
   391  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
   392  		if err != nil {
   393  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   394  		}
   395  		if _, err := patchEndpoint(endpointJSON); !apierrors.IsConflict(err) {
   396  			t.Errorf("Expected error, got %#v", err)
   397  		}
   398  	}
   399  
   400  	// Make sure patch complains about mutating the UID
   401  	{
   402  		endpointTemplate.Name = ""
   403  		endpointTemplate.UID = "abc"
   404  		endpointTemplate.ResourceVersion = ""
   405  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
   406  		if err != nil {
   407  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   408  		}
   409  		if _, err := patchEndpoint(endpointJSON); !apierrors.IsInvalid(err) {
   410  			t.Errorf("Expected error, got %#v", err)
   411  		}
   412  	}
   413  
   414  	// Make sure patch complains about a mismatched name
   415  	{
   416  		endpointTemplate.Name = "changedname"
   417  		endpointTemplate.UID = ""
   418  		endpointTemplate.ResourceVersion = ""
   419  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
   420  		if err != nil {
   421  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   422  		}
   423  		if _, err := patchEndpoint(endpointJSON); !apierrors.IsBadRequest(err) {
   424  			t.Errorf("Expected error, got %#v", err)
   425  		}
   426  	}
   427  
   428  	// Make sure patch containing originally submitted JSON is accepted
   429  	{
   430  		endpointTemplate.Name = ""
   431  		endpointTemplate.UID = ""
   432  		endpointTemplate.ResourceVersion = ""
   433  		endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
   434  		if err != nil {
   435  			t.Fatalf("Failed creating endpoint JSON: %v", err)
   436  		}
   437  		if _, err := patchEndpoint(endpointJSON); err != nil {
   438  			t.Errorf("Failed patching endpoint: %v", err)
   439  		}
   440  	}
   441  }
   442  
   443  func TestAPIVersions(t *testing.T) {
   444  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   445  	defer result.TearDownFn()
   446  
   447  	c := clientset.NewForConfigOrDie(result.ClientConfig)
   448  
   449  	clientVersion := c.CoreV1().RESTClient().APIVersion().String()
   450  	g, err := c.Discovery().ServerGroups()
   451  	if err != nil {
   452  		t.Fatalf("Failed to get api versions: %v", err)
   453  	}
   454  	versions := metav1.ExtractGroupVersions(g)
   455  
   456  	// Verify that the server supports the API version used by the client.
   457  	for _, version := range versions {
   458  		if version == clientVersion {
   459  			return
   460  		}
   461  	}
   462  	t.Errorf("Server does not support APIVersion used by client. Server supported APIVersions: '%v', client APIVersion: '%v'", versions, clientVersion)
   463  }
   464  
   465  func TestEventValidation(t *testing.T) {
   466  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   467  	defer result.TearDownFn()
   468  
   469  	client := clientset.NewForConfigOrDie(result.ClientConfig)
   470  
   471  	createNamespace := func(namespace string) string {
   472  		if namespace == "" {
   473  			namespace = metav1.NamespaceDefault
   474  		}
   475  		return namespace
   476  	}
   477  
   478  	mkCoreEvent := func(ver string, ns string) *v1.Event {
   479  		name := fmt.Sprintf("%v-%v-event", ver, ns)
   480  		namespace := createNamespace(ns)
   481  		return &v1.Event{
   482  			ObjectMeta: metav1.ObjectMeta{
   483  				Namespace: namespace,
   484  				Name:      name,
   485  			},
   486  			InvolvedObject: v1.ObjectReference{
   487  				Namespace: ns,
   488  				Name:      name,
   489  			},
   490  			Count:               2,
   491  			Type:                "Normal",
   492  			ReportingController: "test-controller",
   493  			ReportingInstance:   "test-1",
   494  			Reason:              fmt.Sprintf("event %v test", name),
   495  			Action:              "Testing",
   496  		}
   497  	}
   498  	mkV1Event := func(ver string, ns string) *eventsv1.Event {
   499  		name := fmt.Sprintf("%v-%v-event", ver, ns)
   500  		namespace := createNamespace(ns)
   501  		return &eventsv1.Event{
   502  			ObjectMeta: metav1.ObjectMeta{
   503  				Namespace: namespace,
   504  				Name:      name,
   505  			},
   506  			Regarding: v1.ObjectReference{
   507  				Namespace: ns,
   508  				Name:      name,
   509  			},
   510  			Series: &eventsv1.EventSeries{
   511  				Count:            2,
   512  				LastObservedTime: metav1.MicroTime{Time: time.Now()},
   513  			},
   514  			Type:                "Normal",
   515  			EventTime:           metav1.MicroTime{Time: time.Now()},
   516  			ReportingController: "test-controller",
   517  			ReportingInstance:   "test-2",
   518  			Reason:              fmt.Sprintf("event %v test", name),
   519  			Action:              "Testing",
   520  		}
   521  	}
   522  
   523  	testcases := []struct {
   524  		name      string
   525  		namespace string
   526  		hasError  bool
   527  	}{
   528  		{
   529  			name:      "Involved object is namespaced",
   530  			namespace: "kube-system",
   531  			hasError:  false,
   532  		},
   533  		{
   534  			name:      "Involved object is cluster-scoped",
   535  			namespace: "",
   536  			hasError:  false,
   537  		},
   538  	}
   539  
   540  	for _, test := range testcases {
   541  		// create test
   542  		oldEventObj := mkCoreEvent("corev1", test.namespace)
   543  		corev1Event, err := client.CoreV1().Events(oldEventObj.Namespace).Create(context.TODO(), oldEventObj, metav1.CreateOptions{})
   544  		if err != nil && !test.hasError {
   545  			t.Errorf("%v, call Create failed, expect has error: %v, but got: %v", test.name, test.hasError, err)
   546  		}
   547  		newEventObj := mkV1Event("eventsv1", test.namespace)
   548  		eventsv1Event, err := client.EventsV1().Events(newEventObj.Namespace).Create(context.TODO(), newEventObj, metav1.CreateOptions{})
   549  		if err != nil && !test.hasError {
   550  			t.Errorf("%v, call Create failed, expect has error: %v, but got: %v", test.name, test.hasError, err)
   551  		}
   552  		if corev1Event.Namespace != eventsv1Event.Namespace {
   553  			t.Errorf("%v, events created by different api client have different namespaces that isn't expected", test.name)
   554  		}
   555  		// update test
   556  		corev1Event.Count++
   557  		corev1Event, err = client.CoreV1().Events(corev1Event.Namespace).Update(context.TODO(), corev1Event, metav1.UpdateOptions{})
   558  		if err != nil && !test.hasError {
   559  			t.Errorf("%v, call Update failed, expect has error: %v, but got: %v", test.name, test.hasError, err)
   560  		}
   561  		eventsv1Event.Series.Count++
   562  		eventsv1Event.Series.LastObservedTime = metav1.MicroTime{Time: time.Now()}
   563  		eventsv1Event, err = client.EventsV1().Events(eventsv1Event.Namespace).Update(context.TODO(), eventsv1Event, metav1.UpdateOptions{})
   564  		if err != nil && !test.hasError {
   565  			t.Errorf("%v, call Update failed, expect has error: %v, but got: %v", test.name, test.hasError, err)
   566  		}
   567  		if corev1Event.Namespace != eventsv1Event.Namespace {
   568  			t.Errorf("%v, events updated by different api client have different namespaces that isn't expected", test.name)
   569  		}
   570  	}
   571  }
   572  
   573  func TestEventCompatibility(t *testing.T) {
   574  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   575  	defer result.TearDownFn()
   576  
   577  	client := clientset.NewForConfigOrDie(result.ClientConfig)
   578  
   579  	coreevents := []*v1.Event{
   580  		{
   581  			ObjectMeta: metav1.ObjectMeta{
   582  				Name:      "pass-core-default-cluster-scoped",
   583  				Namespace: "default",
   584  			},
   585  			Type:                "Normal",
   586  			Reason:              "event test",
   587  			Action:              "Testing",
   588  			ReportingController: "test-controller",
   589  			ReportingInstance:   "test-controller-1",
   590  			InvolvedObject:      v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   591  		},
   592  		{
   593  			ObjectMeta: metav1.ObjectMeta{
   594  				Name:      "fail-core-kube-system-cluster-scoped",
   595  				Namespace: "kube-system",
   596  			},
   597  			Type:                "Normal",
   598  			Reason:              "event test",
   599  			Action:              "Testing",
   600  			ReportingController: "test-controller",
   601  			ReportingInstance:   "test-controller-1",
   602  			InvolvedObject:      v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   603  		},
   604  		{
   605  			ObjectMeta: metav1.ObjectMeta{
   606  				Name:      "fail-core-other-ns-cluster-scoped",
   607  				Namespace: "test-ns",
   608  			},
   609  			Type:                "Normal",
   610  			Reason:              "event test",
   611  			Action:              "Testing",
   612  			ReportingController: "test-controller",
   613  			ReportingInstance:   "test-controller-1",
   614  			InvolvedObject:      v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   615  		},
   616  	}
   617  	for _, e := range coreevents {
   618  		t.Run(e.Name, func(t *testing.T) {
   619  			_, err := client.CoreV1().Events(e.Namespace).Create(context.TODO(), e, metav1.CreateOptions{})
   620  			if err == nil && !strings.HasPrefix(e.Name, "pass-") {
   621  				t.Fatalf("unexpected pass")
   622  			}
   623  			if err != nil && !strings.HasPrefix(e.Name, "fail-") {
   624  				t.Fatalf("unexpected error: %v", err)
   625  			}
   626  		})
   627  	}
   628  
   629  	v1events := []*eventsv1.Event{
   630  		{
   631  			ObjectMeta: metav1.ObjectMeta{
   632  				Name:      "pass-events-default-cluster-scoped",
   633  				Namespace: "default",
   634  			},
   635  			EventTime:           metav1.MicroTime{Time: time.Now()},
   636  			Type:                "Normal",
   637  			Reason:              "event test",
   638  			Action:              "Testing",
   639  			ReportingController: "test-controller",
   640  			ReportingInstance:   "test-controller-1",
   641  			Regarding:           v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   642  		},
   643  		{
   644  			ObjectMeta: metav1.ObjectMeta{
   645  				Name:      "pass-events-kube-system-cluster-scoped",
   646  				Namespace: "kube-system",
   647  			},
   648  			EventTime:           metav1.MicroTime{Time: time.Now()},
   649  			Type:                "Normal",
   650  			Reason:              "event test",
   651  			Action:              "Testing",
   652  			ReportingController: "test-controller",
   653  			ReportingInstance:   "test-controller-1",
   654  			Regarding:           v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   655  		},
   656  		{
   657  			ObjectMeta: metav1.ObjectMeta{
   658  				Name:      "fail-events-other-ns-cluster-scoped",
   659  				Namespace: "test-ns",
   660  			},
   661  			EventTime:           metav1.MicroTime{Time: time.Now()},
   662  			Type:                "Normal",
   663  			Reason:              "event test",
   664  			Action:              "Testing",
   665  			ReportingController: "test-controller",
   666  			ReportingInstance:   "test-controller-1",
   667  			Regarding:           v1.ObjectReference{Kind: "Node", Name: "foo", Namespace: ""},
   668  		},
   669  	}
   670  	for _, e := range v1events {
   671  		t.Run(e.Name, func(t *testing.T) {
   672  			_, err := client.EventsV1().Events(e.Namespace).Create(context.TODO(), e, metav1.CreateOptions{})
   673  			if err == nil && !strings.HasPrefix(e.Name, "pass-") {
   674  				t.Fatalf("unexpected pass")
   675  			}
   676  			if err != nil && !strings.HasPrefix(e.Name, "fail-") {
   677  				t.Fatalf("unexpected error: %v", err)
   678  			}
   679  		})
   680  	}
   681  }
   682  
   683  func TestSingleWatch(t *testing.T) {
   684  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   685  	defer result.TearDownFn()
   686  
   687  	client := clientset.NewForConfigOrDie(result.ClientConfig)
   688  
   689  	mkEvent := func(i int) *v1.Event {
   690  		name := fmt.Sprintf("event-%v", i)
   691  		return &v1.Event{
   692  			ObjectMeta: metav1.ObjectMeta{
   693  				Namespace: "default",
   694  				Name:      name,
   695  			},
   696  			InvolvedObject: v1.ObjectReference{
   697  				Namespace: "default",
   698  				Name:      name,
   699  			},
   700  			Reason: fmt.Sprintf("event %v", i),
   701  		}
   702  	}
   703  
   704  	rv1 := ""
   705  	for i := 0; i < 10; i++ {
   706  		event := mkEvent(i)
   707  		got, err := client.CoreV1().Events("default").Create(context.TODO(), event, metav1.CreateOptions{})
   708  		if err != nil {
   709  			t.Fatalf("Failed creating event %#q: %v", event, err)
   710  		}
   711  		if rv1 == "" {
   712  			rv1 = got.ResourceVersion
   713  			if rv1 == "" {
   714  				t.Fatal("did not get a resource version.")
   715  			}
   716  		}
   717  		t.Logf("Created event %#v", got.ObjectMeta)
   718  	}
   719  
   720  	w, err := client.CoreV1().RESTClient().Get().
   721  		Namespace("default").
   722  		Resource("events").
   723  		VersionedParams(&metav1.ListOptions{
   724  			ResourceVersion: rv1,
   725  			Watch:           true,
   726  			FieldSelector:   fields.OneTermEqualSelector("metadata.name", "event-9").String(),
   727  		}, metav1.ParameterCodec).
   728  		Watch(context.TODO())
   729  
   730  	if err != nil {
   731  		t.Fatalf("Failed watch: %v", err)
   732  	}
   733  	defer w.Stop()
   734  
   735  	select {
   736  	case <-time.After(wait.ForeverTestTimeout):
   737  		t.Fatalf("watch took longer than %s", wait.ForeverTestTimeout.String())
   738  	case got, ok := <-w.ResultChan():
   739  		if !ok {
   740  			t.Fatal("Watch channel closed unexpectedly.")
   741  		}
   742  
   743  		// We expect to see an ADD of event-9 and only event-9. (This
   744  		// catches a bug where all the events would have been sent down
   745  		// the channel.)
   746  		if e, a := watch.Added, got.Type; e != a {
   747  			t.Errorf("Wanted %v, got %v", e, a)
   748  		}
   749  		switch o := got.Object.(type) {
   750  		case *v1.Event:
   751  			if e, a := "event-9", o.Name; e != a {
   752  				t.Errorf("Wanted %v, got %v", e, a)
   753  			}
   754  		default:
   755  			t.Fatalf("Unexpected watch event containing object %#q", got)
   756  		}
   757  	}
   758  }
   759  
   760  func TestMultiWatch(t *testing.T) {
   761  	// Disable this test as long as it demonstrates a problem.
   762  	// TODO: Re-enable this test when we get #6059 resolved.
   763  	t.Skip()
   764  
   765  	const watcherCount = 50
   766  	rt.GOMAXPROCS(watcherCount)
   767  
   768  	result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
   769  	defer result.TearDownFn()
   770  
   771  	client := clientset.NewForConfigOrDie(result.ClientConfig)
   772  
   773  	dummyEvent := func(i int) *v1.Event {
   774  		name := fmt.Sprintf("unrelated-%v", i)
   775  		return &v1.Event{
   776  			ObjectMeta: metav1.ObjectMeta{
   777  				Name:      fmt.Sprintf("%v.%x", name, time.Now().UnixNano()),
   778  				Namespace: "default",
   779  			},
   780  			InvolvedObject: v1.ObjectReference{
   781  				Name:      name,
   782  				Namespace: "default",
   783  			},
   784  			Reason: fmt.Sprintf("unrelated change %v", i),
   785  		}
   786  	}
   787  
   788  	type timePair struct {
   789  		t    time.Time
   790  		name string
   791  	}
   792  
   793  	receivedTimes := make(chan timePair, watcherCount*2)
   794  	watchesStarted := sync.WaitGroup{}
   795  
   796  	// make a bunch of pods and watch them
   797  	for i := 0; i < watcherCount; i++ {
   798  		watchesStarted.Add(1)
   799  		name := fmt.Sprintf("multi-watch-%v", i)
   800  		got, err := client.CoreV1().Pods("default").Create(context.TODO(), &v1.Pod{
   801  			ObjectMeta: metav1.ObjectMeta{
   802  				Name:   name,
   803  				Labels: labels.Set{"watchlabel": name},
   804  			},
   805  			Spec: v1.PodSpec{
   806  				Containers: []v1.Container{{
   807  					Name:  "pause",
   808  					Image: imageutils.GetPauseImageName(),
   809  				}},
   810  			},
   811  		}, metav1.CreateOptions{})
   812  
   813  		if err != nil {
   814  			t.Fatalf("Couldn't make %v: %v", name, err)
   815  		}
   816  		go func(name, rv string) {
   817  			options := metav1.ListOptions{
   818  				LabelSelector:   labels.Set{"watchlabel": name}.AsSelector().String(),
   819  				ResourceVersion: rv,
   820  			}
   821  			w, err := client.CoreV1().Pods("default").Watch(context.TODO(), options)
   822  			if err != nil {
   823  				panic(fmt.Sprintf("watch error for %v: %v", name, err))
   824  			}
   825  			defer w.Stop()
   826  			watchesStarted.Done()
   827  			e, ok := <-w.ResultChan() // should get the update (that we'll do below)
   828  			if !ok {
   829  				panic(fmt.Sprintf("%v ended early?", name))
   830  			}
   831  			if e.Type != watch.Modified {
   832  				panic(fmt.Sprintf("Got unexpected watch notification:\n%v: %+v %+v", name, e, e.Object))
   833  			}
   834  			receivedTimes <- timePair{time.Now(), name}
   835  		}(name, got.ObjectMeta.ResourceVersion)
   836  	}
   837  	log.Printf("%v: %v pods made and watchers started", time.Now(), watcherCount)
   838  
   839  	// wait for watches to start before we start spamming the system with
   840  	// objects below, otherwise we'll hit the watch window restriction.
   841  	watchesStarted.Wait()
   842  
   843  	const (
   844  		useEventsAsUnrelatedType = false
   845  		usePodsAsUnrelatedType   = true
   846  	)
   847  
   848  	// make a bunch of unrelated changes in parallel
   849  	if useEventsAsUnrelatedType {
   850  		const unrelatedCount = 3000
   851  		var wg sync.WaitGroup
   852  		defer wg.Wait()
   853  		changeToMake := make(chan int, unrelatedCount*2)
   854  		changeMade := make(chan int, unrelatedCount*2)
   855  		go func() {
   856  			for i := 0; i < unrelatedCount; i++ {
   857  				changeToMake <- i
   858  			}
   859  			close(changeToMake)
   860  		}()
   861  		for i := 0; i < 50; i++ {
   862  			wg.Add(1)
   863  			go func() {
   864  				defer wg.Done()
   865  				for {
   866  					i, ok := <-changeToMake
   867  					if !ok {
   868  						return
   869  					}
   870  					if _, err := client.CoreV1().Events("default").Create(context.TODO(), dummyEvent(i), metav1.CreateOptions{}); err != nil {
   871  						panic(fmt.Sprintf("couldn't make an event: %v", err))
   872  					}
   873  					changeMade <- i
   874  				}
   875  			}()
   876  		}
   877  
   878  		for i := 0; i < 2000; i++ {
   879  			<-changeMade
   880  			if (i+1)%50 == 0 {
   881  				log.Printf("%v: %v unrelated changes made", time.Now(), i+1)
   882  			}
   883  		}
   884  	}
   885  	if usePodsAsUnrelatedType {
   886  		const unrelatedCount = 3000
   887  		var wg sync.WaitGroup
   888  		defer wg.Wait()
   889  		changeToMake := make(chan int, unrelatedCount*2)
   890  		changeMade := make(chan int, unrelatedCount*2)
   891  		go func() {
   892  			for i := 0; i < unrelatedCount; i++ {
   893  				changeToMake <- i
   894  			}
   895  			close(changeToMake)
   896  		}()
   897  		for i := 0; i < 50; i++ {
   898  			wg.Add(1)
   899  			go func() {
   900  				defer wg.Done()
   901  				for {
   902  					i, ok := <-changeToMake
   903  					if !ok {
   904  						return
   905  					}
   906  					name := fmt.Sprintf("unrelated-%v", i)
   907  					_, err := client.CoreV1().Pods("default").Create(context.TODO(), &v1.Pod{
   908  						ObjectMeta: metav1.ObjectMeta{
   909  							Name: name,
   910  						},
   911  						Spec: v1.PodSpec{
   912  							Containers: []v1.Container{{
   913  								Name:  "nothing",
   914  								Image: imageutils.GetPauseImageName(),
   915  							}},
   916  						},
   917  					}, metav1.CreateOptions{})
   918  
   919  					if err != nil {
   920  						panic(fmt.Sprintf("couldn't make unrelated pod: %v", err))
   921  					}
   922  					changeMade <- i
   923  				}
   924  			}()
   925  		}
   926  
   927  		for i := 0; i < 2000; i++ {
   928  			<-changeMade
   929  			if (i+1)%50 == 0 {
   930  				log.Printf("%v: %v unrelated changes made", time.Now(), i+1)
   931  			}
   932  		}
   933  	}
   934  
   935  	// Now we still have changes being made in parallel, but at least 1000 have been made.
   936  	// Make some updates to send down the watches.
   937  	sentTimes := make(chan timePair, watcherCount*2)
   938  	for i := 0; i < watcherCount; i++ {
   939  		go func(i int) {
   940  			name := fmt.Sprintf("multi-watch-%v", i)
   941  			pod, err := client.CoreV1().Pods("default").Get(context.TODO(), name, metav1.GetOptions{})
   942  			if err != nil {
   943  				panic(fmt.Sprintf("Couldn't get %v: %v", name, err))
   944  			}
   945  			pod.Spec.Containers[0].Image = imageutils.GetPauseImageName()
   946  			sentTimes <- timePair{time.Now(), name}
   947  			if _, err := client.CoreV1().Pods("default").Update(context.TODO(), pod, metav1.UpdateOptions{}); err != nil {
   948  				panic(fmt.Sprintf("Couldn't make %v: %v", name, err))
   949  			}
   950  		}(i)
   951  	}
   952  
   953  	sent := map[string]time.Time{}
   954  	for i := 0; i < watcherCount; i++ {
   955  		tp := <-sentTimes
   956  		sent[tp.name] = tp.t
   957  	}
   958  	log.Printf("all changes made")
   959  	dur := map[string]time.Duration{}
   960  	for i := 0; i < watcherCount; i++ {
   961  		tp := <-receivedTimes
   962  		delta := tp.t.Sub(sent[tp.name])
   963  		dur[tp.name] = delta
   964  		log.Printf("%v: %v", tp.name, delta)
   965  	}
   966  	log.Printf("all watches ended")
   967  	t.Errorf("durations: %v", dur)
   968  }
   969  
   970  func TestApplyWithApplyConfiguration(t *testing.T) {
   971  	deployment := appsv1ac.Deployment("nginx-deployment-3", "default").
   972  		WithSpec(appsv1ac.DeploymentSpec().
   973  			WithSelector(metav1ac.LabelSelector().
   974  				WithMatchLabels(map[string]string{"app": "nginx"}),
   975  			).
   976  			WithTemplate(corev1ac.PodTemplateSpec().
   977  				WithLabels(map[string]string{"app": "nginx"}).
   978  				WithSpec(corev1ac.PodSpec().
   979  					WithContainers(corev1ac.Container().
   980  						WithName("nginx").
   981  						WithImage("nginx:1.14.2").
   982  						WithStdin(true).
   983  						WithPorts(corev1ac.ContainerPort().
   984  							WithContainerPort(8080).
   985  							WithProtocol(v1.ProtocolTCP),
   986  						).
   987  						WithResources(corev1ac.ResourceRequirements().
   988  							WithLimits(v1.ResourceList{
   989  								v1.ResourceCPU:    resource.MustParse("4"),
   990  								v1.ResourceMemory: resource.MustParse("32Gi"),
   991  							}),
   992  						),
   993  					),
   994  				),
   995  			),
   996  		)
   997  	testServer := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
   998  	defer testServer.TearDownFn()
   999  
  1000  	c := clientset.NewForConfigOrDie(testServer.ClientConfig)
  1001  
  1002  	// Test apply to spec
  1003  	obj, err := c.AppsV1().Deployments("default").Apply(context.TODO(), deployment, metav1.ApplyOptions{FieldManager: "test-mgr", Force: true})
  1004  	if err != nil {
  1005  		t.Fatalf("unexpected error when applying manifest for Deployment: %v", err)
  1006  	}
  1007  	if obj.Spec.Template.Spec.Containers[0].Image != "nginx:1.14.2" {
  1008  		t.Errorf("expected image %s but got %s", "nginx:1.14.2", obj.Spec.Template.Spec.Containers[0].Image)
  1009  	}
  1010  	cpu := obj.Spec.Template.Spec.Containers[0].Resources.Limits[v1.ResourceCPU]
  1011  	if cpu.Value() != int64(4) {
  1012  		t.Errorf("expected resourceCPU limit %d but got %d", 4, cpu.Value())
  1013  	}
  1014  
  1015  	// Test apply to status
  1016  	statusApply := appsv1ac.Deployment("nginx-deployment-3", "default").
  1017  		WithStatus(appsv1ac.DeploymentStatus().
  1018  			WithConditions(
  1019  				appsv1ac.DeploymentCondition().
  1020  					WithType(appsv1.DeploymentReplicaFailure).
  1021  					WithStatus(v1.ConditionUnknown).
  1022  					WithLastTransitionTime(metav1.Now()).
  1023  					WithLastUpdateTime(metav1.Now()).
  1024  					WithMessage("apply status test").
  1025  					WithReason("TestApplyWithApplyConfiguration"),
  1026  			),
  1027  		)
  1028  	obj, err = c.AppsV1().Deployments("default").ApplyStatus(context.TODO(), statusApply, metav1.ApplyOptions{FieldManager: "test-mgr", Force: true})
  1029  	if err != nil {
  1030  		t.Fatalf("unexpected error when applying manifest for Deployment: %v", err)
  1031  	}
  1032  	var found bool
  1033  	for _, c := range obj.Status.Conditions {
  1034  		if c.Type == appsv1.DeploymentReplicaFailure && c.Status == v1.ConditionUnknown &&
  1035  			c.Message == "apply status test" && c.Reason == "TestApplyWithApplyConfiguration" {
  1036  			found = true
  1037  			break
  1038  		}
  1039  	}
  1040  	if !found {
  1041  		t.Error("expected status to contain DeploymentReplicaFailure condition set by apply")
  1042  	}
  1043  }
  1044  
  1045  func TestExtractModifyApply(t *testing.T) {
  1046  	testCases := []struct {
  1047  		name string
  1048  		// modifyFunc modifies deployApply, defined below, after it is applied and "extracted"
  1049  		// apply is skipped if this func is nil
  1050  		modifyFunc       func(apply *appsv1ac.DeploymentApplyConfiguration)
  1051  		modifyStatusFunc func(apply *appsv1ac.DeploymentApplyConfiguration) // same but for status
  1052  		// verifyAppliedFunc verifies the results of applying the applied
  1053  		// configuration after modifyFunc modifies it. Only called if modifyFunc is provided.
  1054  		verifyAppliedFunc       func(applied *appsv1ac.DeploymentApplyConfiguration)
  1055  		verifyStatusAppliedFunc func(applied *appsv1ac.DeploymentApplyConfiguration) // same but for status
  1056  	}{
  1057  		{
  1058  			// With<fieldname>() on a scalar field replaces it with the given value
  1059  			name: "modify-scalar",
  1060  			modifyFunc: func(apply *appsv1ac.DeploymentApplyConfiguration) {
  1061  				apply.Spec.WithReplicas(2)
  1062  			},
  1063  			verifyAppliedFunc: func(applied *appsv1ac.DeploymentApplyConfiguration) {
  1064  				if *applied.Spec.Replicas != 2 {
  1065  					t.Errorf("Expected 2 replicas but got: %d", *applied.Spec.Replicas)
  1066  				}
  1067  			},
  1068  		},
  1069  		{
  1070  			// With<fieldname>() on a non-empty struct field replaces the entire struct
  1071  			name: "modify-struct",
  1072  			modifyFunc: func(apply *appsv1ac.DeploymentApplyConfiguration) {
  1073  				apply.Spec.Template.WithSpec(corev1ac.PodSpec(). // replace the Spec of the existing Template
  1074  											WithContainers(
  1075  						corev1ac.Container().
  1076  							WithName("modify-struct").
  1077  							WithImage("nginx:1.14.3"),
  1078  					),
  1079  				)
  1080  			},
  1081  			verifyAppliedFunc: func(applied *appsv1ac.DeploymentApplyConfiguration) {
  1082  				containers := applied.Spec.Template.Spec.Containers
  1083  				if len(containers) != 1 {
  1084  					t.Errorf("Expected 1 container but got %d", len(containers))
  1085  				}
  1086  				if *containers[0].Name != "modify-struct" {
  1087  					t.Errorf("Expected container name modify-struct but got: %s", *containers[0].Name)
  1088  				}
  1089  			},
  1090  		},
  1091  		{
  1092  			// With<fieldname>() on a non-empty map field puts all the given entries into the existing map
  1093  			name: "modify-map",
  1094  			modifyFunc: func(apply *appsv1ac.DeploymentApplyConfiguration) {
  1095  				apply.WithLabels(map[string]string{"label2": "value2"})
  1096  			},
  1097  			verifyAppliedFunc: func(applied *appsv1ac.DeploymentApplyConfiguration) {
  1098  				labels := applied.Labels
  1099  				if len(labels) != 2 {
  1100  					t.Errorf("Expected 2 label but got %d", len(labels))
  1101  				}
  1102  				if labels["label2"] != "value2" {
  1103  					t.Errorf("Expected container name value2 but got: %s", labels["label2"])
  1104  				}
  1105  			},
  1106  		},
  1107  		{
  1108  			// With<fieldname>() on a non-empty slice field appends all the given items to the existing slice
  1109  			name: "modify-slice",
  1110  			modifyFunc: func(apply *appsv1ac.DeploymentApplyConfiguration) {
  1111  				apply.Spec.Template.Spec.WithContainers(corev1ac.Container().
  1112  					WithName("modify-slice").
  1113  					WithImage("nginx:1.14.2"),
  1114  				)
  1115  			},
  1116  			verifyAppliedFunc: func(applied *appsv1ac.DeploymentApplyConfiguration) {
  1117  				containers := applied.Spec.Template.Spec.Containers
  1118  				if len(containers) != 2 {
  1119  					t.Errorf("Expected 2 containers but got %d", len(containers))
  1120  				}
  1121  				if *containers[0].Name != "initial-container" {
  1122  					t.Errorf("Expected container name initial-container but got: %s", *containers[0].Name)
  1123  				}
  1124  				if *containers[1].Name != "modify-slice" {
  1125  					t.Errorf("Expected container name modify-slice but got: %s", *containers[1].Name)
  1126  				}
  1127  			},
  1128  		},
  1129  		{
  1130  			// Append a condition to the status if the object
  1131  			name: "modify-status-conditions",
  1132  			modifyStatusFunc: func(apply *appsv1ac.DeploymentApplyConfiguration) {
  1133  				apply.WithStatus(appsv1ac.DeploymentStatus().
  1134  					WithConditions(appsv1ac.DeploymentCondition().
  1135  						WithType(appsv1.DeploymentProgressing).
  1136  						WithStatus(v1.ConditionUnknown).
  1137  						WithLastTransitionTime(metav1.Now()).
  1138  						WithLastUpdateTime(metav1.Now()).
  1139  						WithMessage("progressing").
  1140  						WithReason("TestExtractModifyApply_Status"),
  1141  					),
  1142  				)
  1143  			},
  1144  			verifyStatusAppliedFunc: func(applied *appsv1ac.DeploymentApplyConfiguration) {
  1145  				conditions := applied.Status.Conditions
  1146  				if len(conditions) != 1 {
  1147  					t.Errorf("Expected 1 conditions but got %d", len(conditions))
  1148  				}
  1149  				if *conditions[0].Type != appsv1.DeploymentProgressing {
  1150  					t.Errorf("Expected condition name DeploymentProgressing but got: %s", *conditions[0].Type)
  1151  				}
  1152  			},
  1153  		},
  1154  	}
  1155  
  1156  	testServer := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
  1157  	defer testServer.TearDownFn()
  1158  	c := clientset.NewForConfigOrDie(testServer.ClientConfig)
  1159  	deploymentClient := c.AppsV1().Deployments("default")
  1160  	fieldMgr := "test-mgr"
  1161  
  1162  	for _, tc := range testCases {
  1163  		t.Run(tc.name, func(t *testing.T) {
  1164  			// Applied at the started of each test
  1165  			deployApply := appsv1ac.Deployment(tc.name, "default").
  1166  				WithLabels(map[string]string{"label1": "value1"}).
  1167  				WithSpec(appsv1ac.DeploymentSpec().
  1168  					WithSelector(metav1ac.LabelSelector().
  1169  						WithMatchLabels(map[string]string{"app": tc.name}),
  1170  					).
  1171  					WithTemplate(corev1ac.PodTemplateSpec().
  1172  						WithLabels(map[string]string{"app": tc.name}).
  1173  						WithSpec(corev1ac.PodSpec().
  1174  							WithContainers(
  1175  								corev1ac.Container().
  1176  									WithName("initial-container").
  1177  									WithImage("nginx:1.14.2"),
  1178  							),
  1179  						),
  1180  					),
  1181  				)
  1182  			actual, err := deploymentClient.Apply(context.TODO(), deployApply, metav1.ApplyOptions{FieldManager: fieldMgr})
  1183  			if err != nil {
  1184  				t.Fatalf("Failed to apply: %v", err)
  1185  			}
  1186  			if tc.modifyFunc != nil {
  1187  				extractedDeployment, err := appsv1ac.ExtractDeployment(actual, fieldMgr)
  1188  				if err != nil {
  1189  					t.Fatalf("Failed to extract: %v", err)
  1190  				}
  1191  				tc.modifyFunc(extractedDeployment)
  1192  				result, err := deploymentClient.Apply(context.TODO(), extractedDeployment, metav1.ApplyOptions{FieldManager: fieldMgr})
  1193  				if err != nil {
  1194  					t.Fatalf("Failed to apply extracted apply configuration: %v", err)
  1195  				}
  1196  				extractedResult, err := appsv1ac.ExtractDeployment(result, fieldMgr)
  1197  				if err != nil {
  1198  					t.Fatalf("Failed to extract: %v", err)
  1199  				}
  1200  				if tc.verifyAppliedFunc != nil {
  1201  					tc.verifyAppliedFunc(extractedResult)
  1202  				}
  1203  			}
  1204  
  1205  			if tc.modifyStatusFunc != nil {
  1206  				extractedDeployment, err := appsv1ac.ExtractDeploymentStatus(actual, fieldMgr)
  1207  				if err != nil {
  1208  					t.Fatalf("Failed to extract: %v", err)
  1209  				}
  1210  				tc.modifyStatusFunc(extractedDeployment)
  1211  				result, err := deploymentClient.ApplyStatus(context.TODO(), extractedDeployment, metav1.ApplyOptions{FieldManager: fieldMgr})
  1212  				if err != nil {
  1213  					t.Fatalf("Failed to apply extracted apply configuration to status: %v", err)
  1214  				}
  1215  				extractedResult, err := appsv1ac.ExtractDeploymentStatus(result, fieldMgr)
  1216  				if err != nil {
  1217  					t.Fatalf("Failed to extract: %v", err)
  1218  				}
  1219  				if tc.verifyStatusAppliedFunc != nil {
  1220  					tc.verifyStatusAppliedFunc(extractedResult)
  1221  				}
  1222  			}
  1223  		})
  1224  	}
  1225  }
  1226  
  1227  func TestExtractModifyApply_ForceOwnership(t *testing.T) {
  1228  	testServer := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins", "ServiceAccount"}, framework.SharedEtcd())
  1229  	defer testServer.TearDownFn()
  1230  	c := clientset.NewForConfigOrDie(testServer.ClientConfig)
  1231  	deploymentClient := c.AppsV1().Deployments("default")
  1232  
  1233  	// apply an initial state with one field manager
  1234  	createApply := appsv1ac.Deployment("nginx-apply", "default").
  1235  		WithSpec(appsv1ac.DeploymentSpec().
  1236  			WithSelector(metav1ac.LabelSelector().
  1237  				WithMatchLabels(map[string]string{"app": "nginx"}),
  1238  			).
  1239  			WithTemplate(corev1ac.PodTemplateSpec().
  1240  				WithLabels(map[string]string{"app": "nginx"}).
  1241  				WithSpec(corev1ac.PodSpec().
  1242  					WithContainers(
  1243  						corev1ac.Container().
  1244  							WithName("nginx").
  1245  							WithImage("nginx:1.14.2").
  1246  							WithWorkingDir("/tmp/v1"),
  1247  					),
  1248  				),
  1249  			),
  1250  		)
  1251  
  1252  	_, err := deploymentClient.Apply(context.TODO(), createApply, metav1.ApplyOptions{FieldManager: "create-mgr", Force: true})
  1253  	if err != nil {
  1254  		t.Fatalf("Error creating createApply: %v", err)
  1255  	}
  1256  
  1257  	// apply some non-overlapping fields with another field manager
  1258  	sidecarApply := appsv1ac.Deployment("nginx-apply", "default").
  1259  		WithSpec(appsv1ac.DeploymentSpec().
  1260  			WithTemplate(corev1ac.PodTemplateSpec().
  1261  				WithSpec(corev1ac.PodSpec().
  1262  					WithContainers(
  1263  						corev1ac.Container().
  1264  							WithName("sidecar").
  1265  							WithImage("nginx:1.14.2"),
  1266  					),
  1267  				),
  1268  			),
  1269  		)
  1270  
  1271  	applied, err := deploymentClient.Apply(context.TODO(), sidecarApply, metav1.ApplyOptions{FieldManager: "sidecar-mgr", Force: true})
  1272  	if err != nil {
  1273  		t.Fatalf("Error applying createApply: %v", err)
  1274  	}
  1275  	sidecarExtracted, err := appsv1ac.ExtractDeployment(applied, "sidecar-mgr")
  1276  	if err != nil {
  1277  		t.Fatalf("Error extracting createApply apply configuration: %v", err)
  1278  	}
  1279  	if !equality.Semantic.DeepEqual(sidecarApply, sidecarExtracted) {
  1280  		t.Errorf("Expected sidecarExtracted apply configuration to match original, but got:\n%s\n", cmp.Diff(sidecarApply, sidecarExtracted))
  1281  	}
  1282  
  1283  	// modify the extracted apply configuration that was just applied and add some fields that overlap
  1284  	// with the fields owned by the other field manager to force ownership of them
  1285  	sidecarExtracted.Spec.Template.Spec.Containers[0].WithImage("nginx:1.14.3")
  1286  	sidecarExtracted.Spec.Template.Spec.WithContainers(corev1ac.Container().
  1287  		WithName("nginx").
  1288  		WithWorkingDir("/tmp/v2"),
  1289  	)
  1290  	reapplied, err := deploymentClient.Apply(context.TODO(), sidecarExtracted, metav1.ApplyOptions{FieldManager: "sidecar-mgr", Force: true})
  1291  	if err != nil {
  1292  		t.Fatalf("Unexpected error when applying manifest for Deployment: %v", err)
  1293  	}
  1294  
  1295  	// extract apply configurations for both field managers and check that they are what we expect
  1296  	reappliedExtracted, err := appsv1ac.ExtractDeployment(reapplied, "sidecar-mgr")
  1297  	if err != nil {
  1298  		t.Fatalf("Error extracting sidecarExtracted apply configuration: %v", err)
  1299  	}
  1300  
  1301  	expectedReappliedExtracted := appsv1ac.Deployment("nginx-apply", "default").
  1302  		WithSpec(appsv1ac.DeploymentSpec().
  1303  			WithTemplate(corev1ac.PodTemplateSpec().
  1304  				WithSpec(corev1ac.PodSpec().
  1305  					WithContainers(
  1306  						corev1ac.Container().
  1307  							WithName("sidecar").
  1308  							WithImage("nginx:1.14.3"),
  1309  						corev1ac.Container().
  1310  							WithName("nginx").
  1311  							WithWorkingDir("/tmp/v2"),
  1312  					),
  1313  				),
  1314  			),
  1315  		)
  1316  	if !equality.Semantic.DeepEqual(expectedReappliedExtracted, reappliedExtracted) {
  1317  		t.Errorf("Reapplied apply configuration did not match expected, got:\n%s\n", cmp.Diff(expectedReappliedExtracted, reappliedExtracted))
  1318  	}
  1319  
  1320  	createMgrExtracted, err := appsv1ac.ExtractDeployment(reapplied, "create-mgr")
  1321  	if err != nil {
  1322  		t.Fatalf("Error extracting createApply apply configuration: %v", err)
  1323  	}
  1324  
  1325  	expectedCreateExtracted := appsv1ac.Deployment("nginx-apply", "default").
  1326  		WithSpec(appsv1ac.DeploymentSpec().
  1327  			WithSelector(metav1ac.LabelSelector().
  1328  				WithMatchLabels(map[string]string{"app": "nginx"}),
  1329  			).
  1330  			WithTemplate(corev1ac.PodTemplateSpec().
  1331  				WithLabels(map[string]string{"app": "nginx"}).
  1332  				WithSpec(corev1ac.PodSpec().
  1333  					WithContainers(
  1334  						corev1ac.Container().
  1335  							WithName("nginx").
  1336  							WithImage("nginx:1.14.2"),
  1337  					),
  1338  				),
  1339  			),
  1340  		)
  1341  	if !equality.Semantic.DeepEqual(expectedCreateExtracted, createMgrExtracted) {
  1342  		t.Errorf("createMgrExtracted apply configuration did not match expected, got:\n%s\n", cmp.Diff(expectedCreateExtracted, createMgrExtracted))
  1343  	}
  1344  }
  1345  

View as plain text