...

Source file src/sigs.k8s.io/controller-runtime/pkg/client/fake/client_test.go

Documentation: sigs.k8s.io/controller-runtime/pkg/client/fake

     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 fake
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"strconv"
    24  	"time"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	. "github.com/onsi/ginkgo/v2"
    28  	. "github.com/onsi/gomega"
    29  	appsv1 "k8s.io/api/apps/v1"
    30  	coordinationv1 "k8s.io/api/coordination/v1"
    31  	corev1 "k8s.io/api/core/v1"
    32  	policyv1 "k8s.io/api/policy/v1"
    33  	policyv1beta1 "k8s.io/api/policy/v1beta1"
    34  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    37  	"k8s.io/apimachinery/pkg/fields"
    38  	"k8s.io/apimachinery/pkg/labels"
    39  	"k8s.io/apimachinery/pkg/runtime"
    40  	"k8s.io/apimachinery/pkg/runtime/schema"
    41  	"k8s.io/apimachinery/pkg/types"
    42  	"k8s.io/apimachinery/pkg/watch"
    43  	"k8s.io/client-go/kubernetes/fake"
    44  	"k8s.io/utils/ptr"
    45  
    46  	"sigs.k8s.io/controller-runtime/pkg/client"
    47  	"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
    48  	"sigs.k8s.io/controller-runtime/pkg/scheme"
    49  )
    50  
    51  const (
    52  	machineIDFromStatusUpdate = "machine-id-from-status-update"
    53  	cidrFromStatusUpdate      = "cidr-from-status-update"
    54  )
    55  
    56  var _ = Describe("Fake client", func() {
    57  	var dep *appsv1.Deployment
    58  	var dep2 *appsv1.Deployment
    59  	var cm *corev1.ConfigMap
    60  	var cl client.WithWatch
    61  
    62  	BeforeEach(func() {
    63  		replicas := int32(1)
    64  		dep = &appsv1.Deployment{
    65  			TypeMeta: metav1.TypeMeta{
    66  				APIVersion: "apps/v1",
    67  				Kind:       "Deployment",
    68  			},
    69  			ObjectMeta: metav1.ObjectMeta{
    70  				Name:            "test-deployment",
    71  				Namespace:       "ns1",
    72  				ResourceVersion: trackerAddResourceVersion,
    73  			},
    74  			Spec: appsv1.DeploymentSpec{
    75  				Replicas: &replicas,
    76  				Strategy: appsv1.DeploymentStrategy{
    77  					Type: appsv1.RecreateDeploymentStrategyType,
    78  				},
    79  			},
    80  		}
    81  		dep2 = &appsv1.Deployment{
    82  			TypeMeta: metav1.TypeMeta{
    83  				APIVersion: "apps/v1",
    84  				Kind:       "Deployment",
    85  			},
    86  			ObjectMeta: metav1.ObjectMeta{
    87  				Name:      "test-deployment-2",
    88  				Namespace: "ns1",
    89  				Labels: map[string]string{
    90  					"test-label": "label-value",
    91  				},
    92  				ResourceVersion: trackerAddResourceVersion,
    93  			},
    94  			Spec: appsv1.DeploymentSpec{
    95  				Replicas: &replicas,
    96  			},
    97  		}
    98  		cm = &corev1.ConfigMap{
    99  			TypeMeta: metav1.TypeMeta{
   100  				APIVersion: "v1",
   101  				Kind:       "ConfigMap",
   102  			},
   103  			ObjectMeta: metav1.ObjectMeta{
   104  				Name:            "test-cm",
   105  				Namespace:       "ns2",
   106  				ResourceVersion: trackerAddResourceVersion,
   107  			},
   108  			Data: map[string]string{
   109  				"test-key": "test-value",
   110  			},
   111  		}
   112  	})
   113  
   114  	AssertClientWithoutIndexBehavior := func() {
   115  		It("should be able to Get", func() {
   116  			By("Getting a deployment")
   117  			namespacedName := types.NamespacedName{
   118  				Name:      "test-deployment",
   119  				Namespace: "ns1",
   120  			}
   121  			obj := &appsv1.Deployment{}
   122  			err := cl.Get(context.Background(), namespacedName, obj)
   123  			Expect(err).ToNot(HaveOccurred())
   124  			Expect(obj).To(Equal(dep))
   125  		})
   126  
   127  		It("should be able to Get using unstructured", func() {
   128  			By("Getting a deployment")
   129  			namespacedName := types.NamespacedName{
   130  				Name:      "test-deployment",
   131  				Namespace: "ns1",
   132  			}
   133  			obj := &unstructured.Unstructured{}
   134  			obj.SetAPIVersion("apps/v1")
   135  			obj.SetKind("Deployment")
   136  			err := cl.Get(context.Background(), namespacedName, obj)
   137  			Expect(err).ToNot(HaveOccurred())
   138  		})
   139  
   140  		It("should be able to List", func() {
   141  			By("Listing all deployments in a namespace")
   142  			list := &appsv1.DeploymentList{}
   143  			err := cl.List(context.Background(), list, client.InNamespace("ns1"))
   144  			Expect(err).ToNot(HaveOccurred())
   145  			Expect(list.Items).To(HaveLen(2))
   146  			Expect(list.Items).To(ConsistOf(*dep, *dep2))
   147  		})
   148  
   149  		It("should be able to List using unstructured list", func() {
   150  			By("Listing all deployments in a namespace")
   151  			list := &unstructured.UnstructuredList{}
   152  			list.SetAPIVersion("apps/v1")
   153  			list.SetKind("DeploymentList")
   154  			err := cl.List(context.Background(), list, client.InNamespace("ns1"))
   155  			Expect(err).ToNot(HaveOccurred())
   156  			Expect(list.Items).To(HaveLen(2))
   157  		})
   158  
   159  		It("should be able to List using unstructured list when setting a non-list kind", func() {
   160  			By("Listing all deployments in a namespace")
   161  			list := &unstructured.UnstructuredList{}
   162  			list.SetAPIVersion("apps/v1")
   163  			list.SetKind("Deployment")
   164  			err := cl.List(context.Background(), list, client.InNamespace("ns1"))
   165  			Expect(err).ToNot(HaveOccurred())
   166  			Expect(list.Items).To(HaveLen(2))
   167  		})
   168  
   169  		It("should be able to retrieve registered objects that got manipulated as unstructured", func() {
   170  			list := func() {
   171  				By("Listing all endpoints in a namespace")
   172  				list := &unstructured.UnstructuredList{}
   173  				list.SetAPIVersion("v1")
   174  				list.SetKind("EndpointsList")
   175  				err := cl.List(context.Background(), list, client.InNamespace("ns1"))
   176  				Expect(err).ToNot(HaveOccurred())
   177  				Expect(list.Items).To(HaveLen(1))
   178  			}
   179  
   180  			unstructuredEndpoint := func() *unstructured.Unstructured {
   181  				item := &unstructured.Unstructured{}
   182  				item.SetAPIVersion("v1")
   183  				item.SetKind("Endpoints")
   184  				item.SetName("test-endpoint")
   185  				item.SetNamespace("ns1")
   186  				return item
   187  			}
   188  
   189  			By("Adding the object during client initialization")
   190  			cl = NewClientBuilder().WithRuntimeObjects(unstructuredEndpoint()).Build()
   191  			list()
   192  			Expect(cl.Delete(context.Background(), unstructuredEndpoint())).To(Succeed())
   193  
   194  			By("Creating an object")
   195  			item := unstructuredEndpoint()
   196  			err := cl.Create(context.Background(), item)
   197  			Expect(err).ToNot(HaveOccurred())
   198  			list()
   199  
   200  			By("Updating the object")
   201  			item.SetAnnotations(map[string]string{"foo": "bar"})
   202  			err = cl.Update(context.Background(), item)
   203  			Expect(err).ToNot(HaveOccurred())
   204  			list()
   205  
   206  			By("Patching the object")
   207  			old := item.DeepCopy()
   208  			item.SetAnnotations(map[string]string{"bar": "baz"})
   209  			err = cl.Patch(context.Background(), item, client.MergeFrom(old))
   210  			Expect(err).ToNot(HaveOccurred())
   211  			list()
   212  		})
   213  
   214  		It("should be able to Create an unregistered type using unstructured", func() {
   215  			item := &unstructured.Unstructured{}
   216  			item.SetAPIVersion("custom/v1")
   217  			item.SetKind("Image")
   218  			item.SetName("my-item")
   219  			err := cl.Create(context.Background(), item)
   220  			Expect(err).ToNot(HaveOccurred())
   221  		})
   222  
   223  		It("should be able to Get an unregisted type using unstructured", func() {
   224  			By("Creating an object of an unregistered type")
   225  			item := &unstructured.Unstructured{}
   226  			item.SetAPIVersion("custom/v2")
   227  			item.SetKind("Image")
   228  			item.SetName("my-item")
   229  			err := cl.Create(context.Background(), item)
   230  			Expect(err).ToNot(HaveOccurred())
   231  
   232  			By("Getting and the object")
   233  			item = &unstructured.Unstructured{}
   234  			item.SetAPIVersion("custom/v2")
   235  			item.SetKind("Image")
   236  			item.SetName("my-item")
   237  			err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item)
   238  			Expect(err).ToNot(HaveOccurred())
   239  		})
   240  
   241  		It("should be able to List an unregistered type using unstructured", func() {
   242  			list := &unstructured.UnstructuredList{}
   243  			list.SetAPIVersion("custom/v3")
   244  			list.SetKind("ImageList")
   245  			err := cl.List(context.Background(), list)
   246  			Expect(err).ToNot(HaveOccurred())
   247  		})
   248  
   249  		It("should be able to List an unregistered type using unstructured", func() {
   250  			list := &unstructured.UnstructuredList{}
   251  			list.SetAPIVersion("custom/v4")
   252  			list.SetKind("Image")
   253  			err := cl.List(context.Background(), list)
   254  			Expect(err).ToNot(HaveOccurred())
   255  		})
   256  
   257  		It("should be able to Update an unregistered type using unstructured", func() {
   258  			By("Creating an object of an unregistered type")
   259  			item := &unstructured.Unstructured{}
   260  			item.SetAPIVersion("custom/v5")
   261  			item.SetKind("Image")
   262  			item.SetName("my-item")
   263  			err := cl.Create(context.Background(), item)
   264  			Expect(err).ToNot(HaveOccurred())
   265  
   266  			By("Updating the object")
   267  			err = unstructured.SetNestedField(item.Object, int64(2), "spec", "replicas")
   268  			Expect(err).ToNot(HaveOccurred())
   269  			err = cl.Update(context.Background(), item)
   270  			Expect(err).ToNot(HaveOccurred())
   271  
   272  			By("Getting the object")
   273  			item = &unstructured.Unstructured{}
   274  			item.SetAPIVersion("custom/v5")
   275  			item.SetKind("Image")
   276  			item.SetName("my-item")
   277  			err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item)
   278  			Expect(err).ToNot(HaveOccurred())
   279  
   280  			By("Inspecting the object")
   281  			value, found, err := unstructured.NestedInt64(item.Object, "spec", "replicas")
   282  			Expect(err).ToNot(HaveOccurred())
   283  			Expect(found).To(BeTrue())
   284  			Expect(value).To(Equal(int64(2)))
   285  		})
   286  
   287  		It("should be able to Patch an unregistered type using unstructured", func() {
   288  			By("Creating an object of an unregistered type")
   289  			item := &unstructured.Unstructured{}
   290  			item.SetAPIVersion("custom/v6")
   291  			item.SetKind("Image")
   292  			item.SetName("my-item")
   293  			err := cl.Create(context.Background(), item)
   294  			Expect(err).ToNot(HaveOccurred())
   295  
   296  			By("Updating the object")
   297  			original := item.DeepCopy()
   298  			err = unstructured.SetNestedField(item.Object, int64(2), "spec", "replicas")
   299  			Expect(err).ToNot(HaveOccurred())
   300  			err = cl.Patch(context.Background(), item, client.MergeFrom(original))
   301  			Expect(err).ToNot(HaveOccurred())
   302  
   303  			By("Getting the object")
   304  			item = &unstructured.Unstructured{}
   305  			item.SetAPIVersion("custom/v6")
   306  			item.SetKind("Image")
   307  			item.SetName("my-item")
   308  			err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item)
   309  			Expect(err).ToNot(HaveOccurred())
   310  
   311  			By("Inspecting the object")
   312  			value, found, err := unstructured.NestedInt64(item.Object, "spec", "replicas")
   313  			Expect(err).ToNot(HaveOccurred())
   314  			Expect(found).To(BeTrue())
   315  			Expect(value).To(Equal(int64(2)))
   316  		})
   317  
   318  		It("should be able to Delete an unregistered type using unstructured", func() {
   319  			By("Creating an object of an unregistered type")
   320  			item := &unstructured.Unstructured{}
   321  			item.SetAPIVersion("custom/v7")
   322  			item.SetKind("Image")
   323  			item.SetName("my-item")
   324  			err := cl.Create(context.Background(), item)
   325  			Expect(err).ToNot(HaveOccurred())
   326  
   327  			By("Deleting the object")
   328  			err = cl.Delete(context.Background(), item)
   329  			Expect(err).ToNot(HaveOccurred())
   330  
   331  			By("Getting the object")
   332  			item = &unstructured.Unstructured{}
   333  			item.SetAPIVersion("custom/v7")
   334  			item.SetKind("Image")
   335  			item.SetName("my-item")
   336  			err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item)
   337  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
   338  		})
   339  
   340  		It("should support filtering by labels and their values", func() {
   341  			By("Listing deployments with a particular label and value")
   342  			list := &appsv1.DeploymentList{}
   343  			err := cl.List(context.Background(), list, client.InNamespace("ns1"),
   344  				client.MatchingLabels(map[string]string{
   345  					"test-label": "label-value",
   346  				}))
   347  			Expect(err).ToNot(HaveOccurred())
   348  			Expect(list.Items).To(HaveLen(1))
   349  			Expect(list.Items).To(ConsistOf(*dep2))
   350  		})
   351  
   352  		It("should support filtering by label existence", func() {
   353  			By("Listing deployments with a particular label")
   354  			list := &appsv1.DeploymentList{}
   355  			err := cl.List(context.Background(), list, client.InNamespace("ns1"),
   356  				client.HasLabels{"test-label"})
   357  			Expect(err).ToNot(HaveOccurred())
   358  			Expect(list.Items).To(HaveLen(1))
   359  			Expect(list.Items).To(ConsistOf(*dep2))
   360  		})
   361  
   362  		It("should reject apply patches, they are not supported in the fake client", func() {
   363  			By("Creating a new configmap")
   364  			cm := &corev1.ConfigMap{
   365  				TypeMeta: metav1.TypeMeta{
   366  					APIVersion: "v1",
   367  					Kind:       "ConfigMap",
   368  				},
   369  				ObjectMeta: metav1.ObjectMeta{
   370  					Name:      "new-test-cm",
   371  					Namespace: "ns2",
   372  				},
   373  			}
   374  			err := cl.Create(context.Background(), cm)
   375  			Expect(err).ToNot(HaveOccurred())
   376  
   377  			cm.Data = map[string]string{"foo": "bar"}
   378  			err = cl.Patch(context.Background(), cm, client.Apply, client.ForceOwnership)
   379  			Expect(err).To(MatchError(ContainSubstring("apply patches are not supported in the fake client")))
   380  		})
   381  
   382  		It("should be able to Create", func() {
   383  			By("Creating a new configmap")
   384  			newcm := &corev1.ConfigMap{
   385  				TypeMeta: metav1.TypeMeta{
   386  					APIVersion: "v1",
   387  					Kind:       "ConfigMap",
   388  				},
   389  				ObjectMeta: metav1.ObjectMeta{
   390  					Name:      "new-test-cm",
   391  					Namespace: "ns2",
   392  				},
   393  			}
   394  			err := cl.Create(context.Background(), newcm)
   395  			Expect(err).ToNot(HaveOccurred())
   396  
   397  			By("Getting the new configmap")
   398  			namespacedName := types.NamespacedName{
   399  				Name:      "new-test-cm",
   400  				Namespace: "ns2",
   401  			}
   402  			obj := &corev1.ConfigMap{}
   403  			err = cl.Get(context.Background(), namespacedName, obj)
   404  			Expect(err).ToNot(HaveOccurred())
   405  			Expect(obj).To(Equal(newcm))
   406  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
   407  		})
   408  
   409  		It("should error on create with set resourceVersion", func() {
   410  			By("Creating a new configmap")
   411  			newcm := &corev1.ConfigMap{
   412  				ObjectMeta: metav1.ObjectMeta{
   413  					Name:            "new-test-cm",
   414  					Namespace:       "ns2",
   415  					ResourceVersion: "1",
   416  				},
   417  			}
   418  			err := cl.Create(context.Background(), newcm)
   419  			Expect(apierrors.IsBadRequest(err)).To(BeTrue())
   420  		})
   421  
   422  		It("should not change the submitted object if Create failed", func() {
   423  			By("Trying to create an existing configmap")
   424  			submitted := cm.DeepCopy()
   425  			submitted.ResourceVersion = ""
   426  			submittedReference := submitted.DeepCopy()
   427  			err := cl.Create(context.Background(), submitted)
   428  			Expect(err).To(HaveOccurred())
   429  			Expect(apierrors.IsAlreadyExists(err)).To(BeTrue())
   430  			Expect(submitted).To(Equal(submittedReference))
   431  		})
   432  
   433  		It("should error on Create with empty Name", func() {
   434  			By("Creating a new configmap")
   435  			newcm := &corev1.ConfigMap{
   436  				TypeMeta: metav1.TypeMeta{
   437  					APIVersion: "v1",
   438  					Kind:       "ConfigMap",
   439  				},
   440  				ObjectMeta: metav1.ObjectMeta{
   441  					Namespace: "ns2",
   442  				},
   443  			}
   444  			err := cl.Create(context.Background(), newcm)
   445  			Expect(err.Error()).To(Equal("ConfigMap \"\" is invalid: metadata.name: Required value: name is required"))
   446  		})
   447  
   448  		It("should error on Update with empty Name", func() {
   449  			By("Creating a new configmap")
   450  			newcm := &corev1.ConfigMap{
   451  				TypeMeta: metav1.TypeMeta{
   452  					APIVersion: "v1",
   453  					Kind:       "ConfigMap",
   454  				},
   455  				ObjectMeta: metav1.ObjectMeta{
   456  					Namespace: "ns2",
   457  				},
   458  			}
   459  			err := cl.Update(context.Background(), newcm)
   460  			Expect(err.Error()).To(Equal("ConfigMap \"\" is invalid: metadata.name: Required value: name is required"))
   461  		})
   462  
   463  		It("should be able to Create with GenerateName", func() {
   464  			By("Creating a new configmap")
   465  			newcm := &corev1.ConfigMap{
   466  				TypeMeta: metav1.TypeMeta{
   467  					APIVersion: "v1",
   468  					Kind:       "ConfigMap",
   469  				},
   470  				ObjectMeta: metav1.ObjectMeta{
   471  					GenerateName: "new-test-cm",
   472  					Namespace:    "ns2",
   473  					Labels: map[string]string{
   474  						"test-label": "label-value",
   475  					},
   476  				},
   477  			}
   478  			err := cl.Create(context.Background(), newcm)
   479  			Expect(err).ToNot(HaveOccurred())
   480  
   481  			By("Listing configmaps with a particular label")
   482  			list := &corev1.ConfigMapList{}
   483  			err = cl.List(context.Background(), list, client.InNamespace("ns2"),
   484  				client.MatchingLabels(map[string]string{
   485  					"test-label": "label-value",
   486  				}))
   487  			Expect(err).ToNot(HaveOccurred())
   488  			Expect(list.Items).To(HaveLen(1))
   489  			Expect(list.Items[0].Name).NotTo(BeEmpty())
   490  		})
   491  
   492  		It("should be able to Update", func() {
   493  			By("Updating a new configmap")
   494  			newcm := &corev1.ConfigMap{
   495  				TypeMeta: metav1.TypeMeta{
   496  					APIVersion: "v1",
   497  					Kind:       "ConfigMap",
   498  				},
   499  				ObjectMeta: metav1.ObjectMeta{
   500  					Name:            "test-cm",
   501  					Namespace:       "ns2",
   502  					ResourceVersion: "",
   503  				},
   504  				Data: map[string]string{
   505  					"test-key": "new-value",
   506  				},
   507  			}
   508  			err := cl.Update(context.Background(), newcm)
   509  			Expect(err).ToNot(HaveOccurred())
   510  
   511  			By("Getting the new configmap")
   512  			namespacedName := types.NamespacedName{
   513  				Name:      "test-cm",
   514  				Namespace: "ns2",
   515  			}
   516  			obj := &corev1.ConfigMap{}
   517  			err = cl.Get(context.Background(), namespacedName, obj)
   518  			Expect(err).ToNot(HaveOccurred())
   519  			Expect(obj).To(Equal(newcm))
   520  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1000"))
   521  		})
   522  
   523  		It("should allow updates with non-set ResourceVersion for a resource that allows unconditional updates", func() {
   524  			By("Updating a new configmap")
   525  			newcm := &corev1.ConfigMap{
   526  				TypeMeta: metav1.TypeMeta{
   527  					APIVersion: "v1",
   528  					Kind:       "ConfigMap",
   529  				},
   530  				ObjectMeta: metav1.ObjectMeta{
   531  					Name:      "test-cm",
   532  					Namespace: "ns2",
   533  				},
   534  				Data: map[string]string{
   535  					"test-key": "new-value",
   536  				},
   537  			}
   538  			err := cl.Update(context.Background(), newcm)
   539  			Expect(err).ToNot(HaveOccurred())
   540  
   541  			By("Getting the configmap")
   542  			namespacedName := types.NamespacedName{
   543  				Name:      "test-cm",
   544  				Namespace: "ns2",
   545  			}
   546  			obj := &corev1.ConfigMap{}
   547  			err = cl.Get(context.Background(), namespacedName, obj)
   548  			Expect(err).ToNot(HaveOccurred())
   549  			Expect(obj).To(Equal(newcm))
   550  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1000"))
   551  		})
   552  
   553  		It("should allow patch with non-set ResourceVersion for a resource that doesn't allow unconditional updates", func() {
   554  			schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
   555  			schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
   556  
   557  			scheme := runtime.NewScheme()
   558  			Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
   559  
   560  			cl := NewClientBuilder().WithScheme(scheme).Build()
   561  			original := &WithPointerMeta{
   562  				ObjectMeta: &metav1.ObjectMeta{
   563  					Name:      "obj",
   564  					Namespace: "ns2",
   565  				}}
   566  
   567  			err := cl.Create(context.Background(), original)
   568  			Expect(err).ToNot(HaveOccurred())
   569  
   570  			newObj := &WithPointerMeta{
   571  				ObjectMeta: &metav1.ObjectMeta{
   572  					Name:      original.Name,
   573  					Namespace: original.Namespace,
   574  					Annotations: map[string]string{
   575  						"foo": "bar",
   576  					},
   577  				}}
   578  			Expect(cl.Patch(context.Background(), newObj, client.MergeFrom(original))).To(Succeed())
   579  
   580  			patched := &WithPointerMeta{}
   581  			Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(original), patched)).To(Succeed())
   582  			Expect(patched.Annotations).To(Equal(map[string]string{"foo": "bar"}))
   583  		})
   584  
   585  		It("should reject updates with non-set ResourceVersion for a resource that doesn't allow unconditional updates", func() {
   586  			By("Creating a new binding")
   587  			binding := &corev1.Binding{
   588  				TypeMeta: metav1.TypeMeta{
   589  					APIVersion: "v1",
   590  					Kind:       "Binding",
   591  				},
   592  				ObjectMeta: metav1.ObjectMeta{
   593  					Name:      "test-binding",
   594  					Namespace: "ns2",
   595  				},
   596  				Target: corev1.ObjectReference{
   597  					Kind:       "ConfigMap",
   598  					APIVersion: "v1",
   599  					Namespace:  cm.Namespace,
   600  					Name:       cm.Name,
   601  				},
   602  			}
   603  			Expect(cl.Create(context.Background(), binding)).To(Succeed())
   604  
   605  			By("Updating the binding with a new resource lacking resource version")
   606  			newBinding := &corev1.Binding{
   607  				TypeMeta: metav1.TypeMeta{
   608  					APIVersion: "v1",
   609  					Kind:       "Binding",
   610  				},
   611  				ObjectMeta: metav1.ObjectMeta{
   612  					Name:      binding.Name,
   613  					Namespace: binding.Namespace,
   614  				},
   615  				Target: corev1.ObjectReference{
   616  					Namespace: binding.Namespace,
   617  					Name:      "blue",
   618  				},
   619  			}
   620  			Expect(cl.Update(context.Background(), newBinding)).NotTo(Succeed())
   621  		})
   622  
   623  		It("should allow create on update for a resource that allows create on update", func() {
   624  			By("Creating a new lease with update")
   625  			lease := &coordinationv1.Lease{
   626  				TypeMeta: metav1.TypeMeta{
   627  					APIVersion: "coordination.k8s.io/v1",
   628  					Kind:       "Lease",
   629  				},
   630  				ObjectMeta: metav1.ObjectMeta{
   631  					Name:      "test-lease",
   632  					Namespace: "ns2",
   633  				},
   634  				Spec: coordinationv1.LeaseSpec{},
   635  			}
   636  			Expect(cl.Create(context.Background(), lease)).To(Succeed())
   637  
   638  			By("Getting the lease")
   639  			namespacedName := types.NamespacedName{
   640  				Name:      lease.Name,
   641  				Namespace: lease.Namespace,
   642  			}
   643  			obj := &coordinationv1.Lease{}
   644  			Expect(cl.Get(context.Background(), namespacedName, obj)).To(Succeed())
   645  			Expect(obj).To(Equal(lease))
   646  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1"))
   647  		})
   648  
   649  		It("should reject create on update for a resource that does not allow create on update", func() {
   650  			By("Attemping to create a new configmap with update")
   651  			newcm := &corev1.ConfigMap{
   652  				TypeMeta: metav1.TypeMeta{
   653  					APIVersion: "v1",
   654  					Kind:       "ConfigMap",
   655  				},
   656  				ObjectMeta: metav1.ObjectMeta{
   657  					Name:      "different-test-cm",
   658  					Namespace: "ns2",
   659  				},
   660  				Data: map[string]string{
   661  					"test-key": "new-value",
   662  				},
   663  			}
   664  			Expect(cl.Update(context.Background(), newcm)).NotTo(Succeed())
   665  		})
   666  
   667  		It("should reject updates with non-matching ResourceVersion", func() {
   668  			By("Updating a new configmap")
   669  			newcm := &corev1.ConfigMap{
   670  				ObjectMeta: metav1.ObjectMeta{
   671  					Name:            "test-cm",
   672  					Namespace:       "ns2",
   673  					ResourceVersion: "1",
   674  				},
   675  				Data: map[string]string{
   676  					"test-key": "new-value",
   677  				},
   678  			}
   679  			err := cl.Update(context.Background(), newcm)
   680  			Expect(apierrors.IsConflict(err)).To(BeTrue())
   681  
   682  			By("Getting the configmap")
   683  			namespacedName := types.NamespacedName{
   684  				Name:      "test-cm",
   685  				Namespace: "ns2",
   686  			}
   687  			obj := &corev1.ConfigMap{}
   688  			err = cl.Get(context.Background(), namespacedName, obj)
   689  			Expect(err).ToNot(HaveOccurred())
   690  			Expect(obj).To(Equal(cm))
   691  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal(trackerAddResourceVersion))
   692  		})
   693  
   694  		It("should reject Delete with a mismatched ResourceVersion", func() {
   695  			bogusRV := "bogus"
   696  			By("Deleting with a mismatched ResourceVersion Precondition")
   697  			err := cl.Delete(context.Background(), dep, client.Preconditions{ResourceVersion: &bogusRV})
   698  			Expect(apierrors.IsConflict(err)).To(BeTrue())
   699  
   700  			list := &appsv1.DeploymentList{}
   701  			err = cl.List(context.Background(), list, client.InNamespace("ns1"))
   702  			Expect(err).ToNot(HaveOccurred())
   703  			Expect(list.Items).To(HaveLen(2))
   704  			Expect(list.Items).To(ConsistOf(*dep, *dep2))
   705  		})
   706  
   707  		It("should successfully Delete with a matching ResourceVersion", func() {
   708  			goodRV := trackerAddResourceVersion
   709  			By("Deleting with a matching ResourceVersion Precondition")
   710  			err := cl.Delete(context.Background(), dep, client.Preconditions{ResourceVersion: &goodRV})
   711  			Expect(err).ToNot(HaveOccurred())
   712  
   713  			list := &appsv1.DeploymentList{}
   714  			err = cl.List(context.Background(), list, client.InNamespace("ns1"))
   715  			Expect(err).ToNot(HaveOccurred())
   716  			Expect(list.Items).To(HaveLen(1))
   717  			Expect(list.Items).To(ConsistOf(*dep2))
   718  		})
   719  
   720  		It("should be able to Delete with no ResourceVersion Precondition", func() {
   721  			By("Deleting a deployment")
   722  			err := cl.Delete(context.Background(), dep)
   723  			Expect(err).ToNot(HaveOccurred())
   724  
   725  			By("Listing all deployments in the namespace")
   726  			list := &appsv1.DeploymentList{}
   727  			err = cl.List(context.Background(), list, client.InNamespace("ns1"))
   728  			Expect(err).ToNot(HaveOccurred())
   729  			Expect(list.Items).To(HaveLen(1))
   730  			Expect(list.Items).To(ConsistOf(*dep2))
   731  		})
   732  
   733  		It("should be able to Delete with no opts even if object's ResourceVersion doesn't match server", func() {
   734  			By("Deleting a deployment")
   735  			depCopy := dep.DeepCopy()
   736  			depCopy.ResourceVersion = "bogus"
   737  			err := cl.Delete(context.Background(), depCopy)
   738  			Expect(err).ToNot(HaveOccurred())
   739  
   740  			By("Listing all deployments in the namespace")
   741  			list := &appsv1.DeploymentList{}
   742  			err = cl.List(context.Background(), list, client.InNamespace("ns1"))
   743  			Expect(err).ToNot(HaveOccurred())
   744  			Expect(list.Items).To(HaveLen(1))
   745  			Expect(list.Items).To(ConsistOf(*dep2))
   746  		})
   747  
   748  		It("should handle finalizers on Update", func() {
   749  			namespacedName := types.NamespacedName{
   750  				Name:      "test-cm",
   751  				Namespace: "delete-with-finalizers",
   752  			}
   753  			By("Updating a new object")
   754  			newObj := &corev1.ConfigMap{
   755  				ObjectMeta: metav1.ObjectMeta{
   756  					Name:       namespacedName.Name,
   757  					Namespace:  namespacedName.Namespace,
   758  					Finalizers: []string{"finalizers.sigs.k8s.io/test"},
   759  				},
   760  				Data: map[string]string{
   761  					"test-key": "new-value",
   762  				},
   763  			}
   764  			err := cl.Create(context.Background(), newObj)
   765  			Expect(err).ToNot(HaveOccurred())
   766  
   767  			By("Deleting the object")
   768  			err = cl.Delete(context.Background(), newObj)
   769  			Expect(err).ToNot(HaveOccurred())
   770  
   771  			By("Getting the object")
   772  			obj := &corev1.ConfigMap{}
   773  			err = cl.Get(context.Background(), namespacedName, obj)
   774  			Expect(err).ToNot(HaveOccurred())
   775  			Expect(obj.DeletionTimestamp).NotTo(BeNil())
   776  
   777  			By("Removing the finalizer")
   778  			obj.Finalizers = []string{}
   779  			err = cl.Update(context.Background(), obj)
   780  			Expect(err).ToNot(HaveOccurred())
   781  
   782  			By("Getting the object")
   783  			obj = &corev1.ConfigMap{}
   784  			err = cl.Get(context.Background(), namespacedName, obj)
   785  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
   786  		})
   787  
   788  		It("should reject changes to deletionTimestamp on Update", func() {
   789  			namespacedName := types.NamespacedName{
   790  				Name:      "test-cm",
   791  				Namespace: "reject-with-deletiontimestamp",
   792  			}
   793  			By("Updating a new object")
   794  			newObj := &corev1.ConfigMap{
   795  				ObjectMeta: metav1.ObjectMeta{
   796  					Name:      namespacedName.Name,
   797  					Namespace: namespacedName.Namespace,
   798  				},
   799  				Data: map[string]string{
   800  					"test-key": "new-value",
   801  				},
   802  			}
   803  			err := cl.Create(context.Background(), newObj)
   804  			Expect(err).ToNot(HaveOccurred())
   805  
   806  			By("Getting the object")
   807  			obj := &corev1.ConfigMap{}
   808  			err = cl.Get(context.Background(), namespacedName, obj)
   809  			Expect(err).ToNot(HaveOccurred())
   810  			Expect(obj.DeletionTimestamp).To(BeNil())
   811  
   812  			By("Adding deletionTimestamp")
   813  			now := metav1.Now()
   814  			obj.DeletionTimestamp = &now
   815  			err = cl.Update(context.Background(), obj)
   816  			Expect(err).To(HaveOccurred())
   817  
   818  			By("Deleting the object")
   819  			err = cl.Delete(context.Background(), newObj)
   820  			Expect(err).ToNot(HaveOccurred())
   821  
   822  			By("Changing the deletionTimestamp to new value")
   823  			obj = &corev1.ConfigMap{}
   824  			t := metav1.NewTime(time.Now().Add(time.Second))
   825  			obj.DeletionTimestamp = &t
   826  			err = cl.Update(context.Background(), obj)
   827  			Expect(err).To(HaveOccurred())
   828  
   829  			By("Removing deletionTimestamp")
   830  			obj.DeletionTimestamp = nil
   831  			err = cl.Update(context.Background(), obj)
   832  			Expect(err).To(HaveOccurred())
   833  
   834  		})
   835  
   836  		It("should be able to Delete a Collection", func() {
   837  			By("Deleting a deploymentList")
   838  			err := cl.DeleteAllOf(context.Background(), &appsv1.Deployment{}, client.InNamespace("ns1"))
   839  			Expect(err).ToNot(HaveOccurred())
   840  
   841  			By("Listing all deployments in the namespace")
   842  			list := &appsv1.DeploymentList{}
   843  			err = cl.List(context.Background(), list, client.InNamespace("ns1"))
   844  			Expect(err).ToNot(HaveOccurred())
   845  			Expect(list.Items).To(BeEmpty())
   846  		})
   847  
   848  		It("should handle finalizers deleting a collection", func() {
   849  			for i := 0; i < 5; i++ {
   850  				namespacedName := types.NamespacedName{
   851  					Name:      fmt.Sprintf("test-cm-%d", i),
   852  					Namespace: "delete-collection-with-finalizers",
   853  				}
   854  				By("Creating a new object")
   855  				newObj := &corev1.ConfigMap{
   856  					ObjectMeta: metav1.ObjectMeta{
   857  						Name:       namespacedName.Name,
   858  						Namespace:  namespacedName.Namespace,
   859  						Finalizers: []string{"finalizers.sigs.k8s.io/test"},
   860  					},
   861  					Data: map[string]string{
   862  						"test-key": "new-value",
   863  					},
   864  				}
   865  				err := cl.Create(context.Background(), newObj)
   866  				Expect(err).ToNot(HaveOccurred())
   867  			}
   868  
   869  			By("Deleting the object")
   870  			err := cl.DeleteAllOf(context.Background(), &corev1.ConfigMap{}, client.InNamespace("delete-collection-with-finalizers"))
   871  			Expect(err).ToNot(HaveOccurred())
   872  
   873  			configmaps := corev1.ConfigMapList{}
   874  			err = cl.List(context.Background(), &configmaps, client.InNamespace("delete-collection-with-finalizers"))
   875  			Expect(err).ToNot(HaveOccurred())
   876  
   877  			Expect(configmaps.Items).To(HaveLen(5))
   878  			for _, cm := range configmaps.Items {
   879  				Expect(cm.DeletionTimestamp).NotTo(BeNil())
   880  			}
   881  		})
   882  
   883  		It("should be able to watch", func() {
   884  			By("Creating a watch")
   885  			objWatch, err := cl.Watch(context.Background(), &corev1.ServiceList{})
   886  			Expect(err).NotTo(HaveOccurred())
   887  
   888  			defer objWatch.Stop()
   889  
   890  			go func() {
   891  				defer GinkgoRecover()
   892  				// It is likely starting a new goroutine is slower than progressing
   893  				// in the outer routine, sleep to make sure this is always true
   894  				time.Sleep(100 * time.Millisecond)
   895  
   896  				err := cl.Create(context.Background(), &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "for-watch"}})
   897  				Expect(err).ToNot(HaveOccurred())
   898  			}()
   899  
   900  			event, ok := <-objWatch.ResultChan()
   901  			Expect(ok).To(BeTrue())
   902  			Expect(event.Type).To(Equal(watch.Added))
   903  
   904  			service, ok := event.Object.(*corev1.Service)
   905  			Expect(ok).To(BeTrue())
   906  			Expect(service.Name).To(Equal("for-watch"))
   907  		})
   908  
   909  		Context("with the DryRun option", func() {
   910  			It("should not create a new object", func() {
   911  				By("Creating a new configmap with DryRun")
   912  				newcm := &corev1.ConfigMap{
   913  					ObjectMeta: metav1.ObjectMeta{
   914  						Name:      "new-test-cm",
   915  						Namespace: "ns2",
   916  					},
   917  				}
   918  				err := cl.Create(context.Background(), newcm, client.DryRunAll)
   919  				Expect(err).ToNot(HaveOccurred())
   920  
   921  				By("Getting the new configmap")
   922  				namespacedName := types.NamespacedName{
   923  					Name:      "new-test-cm",
   924  					Namespace: "ns2",
   925  				}
   926  				obj := &corev1.ConfigMap{}
   927  				err = cl.Get(context.Background(), namespacedName, obj)
   928  				Expect(err).To(HaveOccurred())
   929  				Expect(apierrors.IsNotFound(err)).To(BeTrue())
   930  				Expect(obj).NotTo(Equal(newcm))
   931  			})
   932  
   933  			It("should not Update the object", func() {
   934  				By("Updating a new configmap with DryRun")
   935  				newcm := &corev1.ConfigMap{
   936  					ObjectMeta: metav1.ObjectMeta{
   937  						Name:            "test-cm",
   938  						Namespace:       "ns2",
   939  						ResourceVersion: "1",
   940  					},
   941  					Data: map[string]string{
   942  						"test-key": "new-value",
   943  					},
   944  				}
   945  				err := cl.Update(context.Background(), newcm, client.DryRunAll)
   946  				Expect(err).ToNot(HaveOccurred())
   947  
   948  				By("Getting the new configmap")
   949  				namespacedName := types.NamespacedName{
   950  					Name:      "test-cm",
   951  					Namespace: "ns2",
   952  				}
   953  				obj := &corev1.ConfigMap{}
   954  				err = cl.Get(context.Background(), namespacedName, obj)
   955  				Expect(err).ToNot(HaveOccurred())
   956  				Expect(obj).To(Equal(cm))
   957  				Expect(obj.ObjectMeta.ResourceVersion).To(Equal(trackerAddResourceVersion))
   958  			})
   959  
   960  			It("Should not Delete the object", func() {
   961  				By("Deleting a configmap with DryRun with Delete()")
   962  				err := cl.Delete(context.Background(), cm, client.DryRunAll)
   963  				Expect(err).ToNot(HaveOccurred())
   964  
   965  				By("Deleting a configmap with DryRun with DeleteAllOf()")
   966  				err = cl.DeleteAllOf(context.Background(), cm, client.DryRunAll)
   967  				Expect(err).ToNot(HaveOccurred())
   968  
   969  				By("Getting the configmap")
   970  				namespacedName := types.NamespacedName{
   971  					Name:      "test-cm",
   972  					Namespace: "ns2",
   973  				}
   974  				obj := &corev1.ConfigMap{}
   975  				err = cl.Get(context.Background(), namespacedName, obj)
   976  				Expect(err).ToNot(HaveOccurred())
   977  				Expect(obj).To(Equal(cm))
   978  				Expect(obj.ObjectMeta.ResourceVersion).To(Equal(trackerAddResourceVersion))
   979  			})
   980  		})
   981  
   982  		It("should be able to Patch", func() {
   983  			By("Patching a deployment")
   984  			mergePatch, err := json.Marshal(map[string]interface{}{
   985  				"metadata": map[string]interface{}{
   986  					"annotations": map[string]interface{}{
   987  						"foo": "bar",
   988  					},
   989  				},
   990  			})
   991  			Expect(err).NotTo(HaveOccurred())
   992  			err = cl.Patch(context.Background(), dep, client.RawPatch(types.StrategicMergePatchType, mergePatch))
   993  			Expect(err).NotTo(HaveOccurred())
   994  
   995  			By("Getting the patched deployment")
   996  			namespacedName := types.NamespacedName{
   997  				Name:      "test-deployment",
   998  				Namespace: "ns1",
   999  			}
  1000  			obj := &appsv1.Deployment{}
  1001  			err = cl.Get(context.Background(), namespacedName, obj)
  1002  			Expect(err).NotTo(HaveOccurred())
  1003  			Expect(obj.Annotations["foo"]).To(Equal("bar"))
  1004  			Expect(obj.ObjectMeta.ResourceVersion).To(Equal("1000"))
  1005  		})
  1006  
  1007  		It("should ignore deletionTimestamp without finalizer on Create", func() {
  1008  			namespacedName := types.NamespacedName{
  1009  				Name:      "test-cm",
  1010  				Namespace: "ignore-deletiontimestamp",
  1011  			}
  1012  			By("Creating a new object")
  1013  			now := metav1.Now()
  1014  			newObj := &corev1.ConfigMap{
  1015  				ObjectMeta: metav1.ObjectMeta{
  1016  					Name:              namespacedName.Name,
  1017  					Namespace:         namespacedName.Namespace,
  1018  					Finalizers:        []string{"finalizers.sigs.k8s.io/test"},
  1019  					DeletionTimestamp: &now,
  1020  				},
  1021  				Data: map[string]string{
  1022  					"test-key": "new-value",
  1023  				},
  1024  			}
  1025  
  1026  			err := cl.Create(context.Background(), newObj)
  1027  			Expect(err).ToNot(HaveOccurred())
  1028  
  1029  			By("Getting the object")
  1030  			obj := &corev1.ConfigMap{}
  1031  			err = cl.Get(context.Background(), namespacedName, obj)
  1032  			Expect(err).ToNot(HaveOccurred())
  1033  			Expect(obj.DeletionTimestamp).To(BeNil())
  1034  
  1035  		})
  1036  
  1037  		It("should reject deletionTimestamp without finalizers on Build", func() {
  1038  			namespacedName := types.NamespacedName{
  1039  				Name:      "test-cm",
  1040  				Namespace: "reject-deletiontimestamp-no-finalizers",
  1041  			}
  1042  			By("Build with a new object without finalizer")
  1043  			now := metav1.Now()
  1044  			obj := &corev1.ConfigMap{
  1045  				ObjectMeta: metav1.ObjectMeta{
  1046  					Name:              namespacedName.Name,
  1047  					Namespace:         namespacedName.Namespace,
  1048  					DeletionTimestamp: &now,
  1049  				},
  1050  				Data: map[string]string{
  1051  					"test-key": "new-value",
  1052  				},
  1053  			}
  1054  
  1055  			Expect(func() { NewClientBuilder().WithObjects(obj).Build() }).To(Panic())
  1056  
  1057  			By("Build with a new object with finalizer")
  1058  			newObj := &corev1.ConfigMap{
  1059  				ObjectMeta: metav1.ObjectMeta{
  1060  					Name:              namespacedName.Name,
  1061  					Namespace:         namespacedName.Namespace,
  1062  					Finalizers:        []string{"finalizers.sigs.k8s.io/test"},
  1063  					DeletionTimestamp: &now,
  1064  				},
  1065  				Data: map[string]string{
  1066  					"test-key": "new-value",
  1067  				},
  1068  			}
  1069  
  1070  			cl := NewClientBuilder().WithObjects(newObj).Build()
  1071  
  1072  			By("Getting the object")
  1073  			obj = &corev1.ConfigMap{}
  1074  			err := cl.Get(context.Background(), namespacedName, obj)
  1075  			Expect(err).ToNot(HaveOccurred())
  1076  
  1077  		})
  1078  
  1079  		It("should reject changes to deletionTimestamp on Patch", func() {
  1080  			namespacedName := types.NamespacedName{
  1081  				Name:      "test-cm",
  1082  				Namespace: "reject-deletiontimestamp",
  1083  			}
  1084  			By("Creating a new object")
  1085  			now := metav1.Now()
  1086  			newObj := &corev1.ConfigMap{
  1087  				ObjectMeta: metav1.ObjectMeta{
  1088  					Name:       namespacedName.Name,
  1089  					Namespace:  namespacedName.Namespace,
  1090  					Finalizers: []string{"finalizers.sigs.k8s.io/test"},
  1091  				},
  1092  				Data: map[string]string{
  1093  					"test-key": "new-value",
  1094  				},
  1095  			}
  1096  			err := cl.Create(context.Background(), newObj)
  1097  			Expect(err).ToNot(HaveOccurred())
  1098  
  1099  			By("Add a deletionTimestamp")
  1100  			obj := &corev1.ConfigMap{
  1101  				ObjectMeta: metav1.ObjectMeta{
  1102  					Name:              namespacedName.Name,
  1103  					Namespace:         namespacedName.Namespace,
  1104  					Finalizers:        []string{},
  1105  					DeletionTimestamp: &now,
  1106  				},
  1107  			}
  1108  			err = cl.Patch(context.Background(), obj, client.MergeFrom(newObj))
  1109  			Expect(err).To(HaveOccurred())
  1110  
  1111  			By("Deleting the object")
  1112  			err = cl.Delete(context.Background(), newObj)
  1113  			Expect(err).ToNot(HaveOccurred())
  1114  
  1115  			By("Getting the object")
  1116  			obj = &corev1.ConfigMap{}
  1117  			err = cl.Get(context.Background(), namespacedName, obj)
  1118  			Expect(err).ToNot(HaveOccurred())
  1119  			Expect(obj.DeletionTimestamp).NotTo(BeNil())
  1120  
  1121  			By("Changing the deletionTimestamp to new value")
  1122  			newObj = &corev1.ConfigMap{}
  1123  			t := metav1.NewTime(time.Now().Add(time.Second))
  1124  			newObj.DeletionTimestamp = &t
  1125  			err = cl.Patch(context.Background(), newObj, client.MergeFrom(obj))
  1126  			Expect(err).To(HaveOccurred())
  1127  
  1128  			By("Removing deletionTimestamp")
  1129  			newObj = &corev1.ConfigMap{
  1130  				ObjectMeta: metav1.ObjectMeta{
  1131  					Name:              namespacedName.Name,
  1132  					Namespace:         namespacedName.Namespace,
  1133  					DeletionTimestamp: nil,
  1134  				},
  1135  			}
  1136  			err = cl.Patch(context.Background(), newObj, client.MergeFrom(obj))
  1137  			Expect(err).To(HaveOccurred())
  1138  
  1139  		})
  1140  
  1141  		It("should handle finalizers on Patch", func() {
  1142  			namespacedName := types.NamespacedName{
  1143  				Name:      "test-cm",
  1144  				Namespace: "delete-with-finalizers",
  1145  			}
  1146  			By("Creating a new object")
  1147  			newObj := &corev1.ConfigMap{
  1148  				ObjectMeta: metav1.ObjectMeta{
  1149  					Name:       namespacedName.Name,
  1150  					Namespace:  namespacedName.Namespace,
  1151  					Finalizers: []string{"finalizers.sigs.k8s.io/test"},
  1152  				},
  1153  				Data: map[string]string{
  1154  					"test-key": "new-value",
  1155  				},
  1156  			}
  1157  			err := cl.Create(context.Background(), newObj)
  1158  			Expect(err).ToNot(HaveOccurred())
  1159  
  1160  			By("Deleting the object")
  1161  			err = cl.Delete(context.Background(), newObj)
  1162  			Expect(err).ToNot(HaveOccurred())
  1163  
  1164  			By("Removing the finalizer")
  1165  			obj := &corev1.ConfigMap{
  1166  				ObjectMeta: metav1.ObjectMeta{
  1167  					Name:       namespacedName.Name,
  1168  					Namespace:  namespacedName.Namespace,
  1169  					Finalizers: []string{},
  1170  				},
  1171  			}
  1172  			err = cl.Patch(context.Background(), obj, client.MergeFrom(newObj))
  1173  			Expect(err).ToNot(HaveOccurred())
  1174  
  1175  			By("Getting the object")
  1176  			obj = &corev1.ConfigMap{}
  1177  			err = cl.Get(context.Background(), namespacedName, obj)
  1178  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1179  		})
  1180  
  1181  		It("should remove finalizers of the object on Patch", func() {
  1182  			namespacedName := types.NamespacedName{
  1183  				Name:      "test-cm",
  1184  				Namespace: "patch-finalizers-in-obj",
  1185  			}
  1186  			By("Creating a new object")
  1187  			obj := &corev1.ConfigMap{
  1188  				ObjectMeta: metav1.ObjectMeta{
  1189  					Name:       namespacedName.Name,
  1190  					Namespace:  namespacedName.Namespace,
  1191  					Finalizers: []string{"finalizers.sigs.k8s.io/test"},
  1192  				},
  1193  				Data: map[string]string{
  1194  					"test-key": "new-value",
  1195  				},
  1196  			}
  1197  			err := cl.Create(context.Background(), obj)
  1198  			Expect(err).ToNot(HaveOccurred())
  1199  
  1200  			By("Removing the finalizer")
  1201  			mergePatch, err := json.Marshal(map[string]interface{}{
  1202  				"metadata": map[string]interface{}{
  1203  					"$deleteFromPrimitiveList/finalizers": []string{
  1204  						"finalizers.sigs.k8s.io/test",
  1205  					},
  1206  				},
  1207  			})
  1208  			Expect(err).ToNot(HaveOccurred())
  1209  			err = cl.Patch(context.Background(), obj, client.RawPatch(types.StrategicMergePatchType, mergePatch))
  1210  			Expect(err).ToNot(HaveOccurred())
  1211  
  1212  			By("Check the finalizer has been removed in the object")
  1213  			Expect(obj.Finalizers).To(BeEmpty())
  1214  
  1215  			By("Check the finalizer has been removed in client")
  1216  			newObj := &corev1.ConfigMap{}
  1217  			err = cl.Get(context.Background(), namespacedName, newObj)
  1218  			Expect(err).ToNot(HaveOccurred())
  1219  			Expect(newObj.Finalizers).To(BeEmpty())
  1220  		})
  1221  
  1222  	}
  1223  
  1224  	Context("with default scheme.Scheme", func() {
  1225  		BeforeEach(func() {
  1226  			cl = NewClientBuilder().
  1227  				WithObjects(dep, dep2, cm).
  1228  				Build()
  1229  		})
  1230  		AssertClientWithoutIndexBehavior()
  1231  	})
  1232  
  1233  	Context("with given scheme", func() {
  1234  		BeforeEach(func() {
  1235  			scheme := runtime.NewScheme()
  1236  			Expect(corev1.AddToScheme(scheme)).To(Succeed())
  1237  			Expect(appsv1.AddToScheme(scheme)).To(Succeed())
  1238  			Expect(coordinationv1.AddToScheme(scheme)).To(Succeed())
  1239  			cl = NewClientBuilder().
  1240  				WithScheme(scheme).
  1241  				WithObjects(cm).
  1242  				WithLists(&appsv1.DeploymentList{Items: []appsv1.Deployment{*dep, *dep2}}).
  1243  				Build()
  1244  		})
  1245  		AssertClientWithoutIndexBehavior()
  1246  	})
  1247  
  1248  	Context("with Indexes", func() {
  1249  		depReplicasIndexer := func(obj client.Object) []string {
  1250  			dep, ok := obj.(*appsv1.Deployment)
  1251  			if !ok {
  1252  				panic(fmt.Errorf("indexer function for type %T's spec.replicas field received"+
  1253  					" object of type %T, this should never happen", appsv1.Deployment{}, obj))
  1254  			}
  1255  			indexVal := ""
  1256  			if dep.Spec.Replicas != nil {
  1257  				indexVal = strconv.Itoa(int(*dep.Spec.Replicas))
  1258  			}
  1259  			return []string{indexVal}
  1260  		}
  1261  
  1262  		depStrategyTypeIndexer := func(obj client.Object) []string {
  1263  			dep, ok := obj.(*appsv1.Deployment)
  1264  			if !ok {
  1265  				panic(fmt.Errorf("indexer function for type %T's spec.strategy.type field received"+
  1266  					" object of type %T, this should never happen", appsv1.Deployment{}, obj))
  1267  			}
  1268  			return []string{string(dep.Spec.Strategy.Type)}
  1269  		}
  1270  
  1271  		var cb *ClientBuilder
  1272  		BeforeEach(func() {
  1273  			cb = NewClientBuilder().
  1274  				WithObjects(dep, dep2, cm).
  1275  				WithIndex(&appsv1.Deployment{}, "spec.replicas", depReplicasIndexer)
  1276  		})
  1277  
  1278  		Context("client has just one Index", func() {
  1279  			BeforeEach(func() { cl = cb.Build() })
  1280  
  1281  			Context("behavior that doesn't use an Index", func() {
  1282  				AssertClientWithoutIndexBehavior()
  1283  			})
  1284  
  1285  			Context("filtered List using field selector", func() {
  1286  				It("errors when there's no Index for the GroupVersionResource", func() {
  1287  					listOpts := &client.ListOptions{
  1288  						FieldSelector: fields.OneTermEqualSelector("key", "val"),
  1289  					}
  1290  					err := cl.List(context.Background(), &corev1.ConfigMapList{}, listOpts)
  1291  					Expect(err).To(HaveOccurred())
  1292  				})
  1293  
  1294  				It("errors when there's no Index matching the field name", func() {
  1295  					listOpts := &client.ListOptions{
  1296  						FieldSelector: fields.OneTermEqualSelector("spec.paused", "false"),
  1297  					}
  1298  					err := cl.List(context.Background(), &appsv1.DeploymentList{}, listOpts)
  1299  					Expect(err).To(HaveOccurred())
  1300  				})
  1301  
  1302  				It("errors when field selector uses two requirements", func() {
  1303  					listOpts := &client.ListOptions{
  1304  						FieldSelector: fields.AndSelectors(
  1305  							fields.OneTermEqualSelector("spec.replicas", "1"),
  1306  							fields.OneTermEqualSelector("spec.strategy.type", string(appsv1.RecreateDeploymentStrategyType)),
  1307  						)}
  1308  					err := cl.List(context.Background(), &appsv1.DeploymentList{}, listOpts)
  1309  					Expect(err).To(HaveOccurred())
  1310  				})
  1311  
  1312  				It("returns two deployments that match the only field selector requirement", func() {
  1313  					listOpts := &client.ListOptions{
  1314  						FieldSelector: fields.OneTermEqualSelector("spec.replicas", "1"),
  1315  					}
  1316  					list := &appsv1.DeploymentList{}
  1317  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1318  					Expect(list.Items).To(ConsistOf(*dep, *dep2))
  1319  				})
  1320  
  1321  				It("returns no object because no object matches the only field selector requirement", func() {
  1322  					listOpts := &client.ListOptions{
  1323  						FieldSelector: fields.OneTermEqualSelector("spec.replicas", "2"),
  1324  					}
  1325  					list := &appsv1.DeploymentList{}
  1326  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1327  					Expect(list.Items).To(BeEmpty())
  1328  				})
  1329  
  1330  				It("returns deployment that matches both the field and label selectors", func() {
  1331  					listOpts := &client.ListOptions{
  1332  						FieldSelector: fields.OneTermEqualSelector("spec.replicas", "1"),
  1333  						LabelSelector: labels.SelectorFromSet(dep2.Labels),
  1334  					}
  1335  					list := &appsv1.DeploymentList{}
  1336  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1337  					Expect(list.Items).To(ConsistOf(*dep2))
  1338  				})
  1339  
  1340  				It("returns no object even if field selector matches because label selector doesn't", func() {
  1341  					listOpts := &client.ListOptions{
  1342  						FieldSelector: fields.OneTermEqualSelector("spec.replicas", "1"),
  1343  						LabelSelector: labels.Nothing(),
  1344  					}
  1345  					list := &appsv1.DeploymentList{}
  1346  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1347  					Expect(list.Items).To(BeEmpty())
  1348  				})
  1349  
  1350  				It("returns no object even if label selector matches because field selector doesn't", func() {
  1351  					listOpts := &client.ListOptions{
  1352  						FieldSelector: fields.OneTermEqualSelector("spec.replicas", "2"),
  1353  						LabelSelector: labels.Everything(),
  1354  					}
  1355  					list := &appsv1.DeploymentList{}
  1356  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1357  					Expect(list.Items).To(BeEmpty())
  1358  				})
  1359  			})
  1360  		})
  1361  
  1362  		Context("client has two Indexes", func() {
  1363  			BeforeEach(func() {
  1364  				cl = cb.WithIndex(&appsv1.Deployment{}, "spec.strategy.type", depStrategyTypeIndexer).Build()
  1365  			})
  1366  
  1367  			Context("behavior that doesn't use an Index", func() {
  1368  				AssertClientWithoutIndexBehavior()
  1369  			})
  1370  
  1371  			Context("filtered List using field selector", func() {
  1372  				It("uses the second index to retrieve the indexed objects when there are matches", func() {
  1373  					listOpts := &client.ListOptions{
  1374  						FieldSelector: fields.OneTermEqualSelector("spec.strategy.type", string(appsv1.RecreateDeploymentStrategyType)),
  1375  					}
  1376  					list := &appsv1.DeploymentList{}
  1377  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1378  					Expect(list.Items).To(ConsistOf(*dep))
  1379  				})
  1380  
  1381  				It("uses the second index to retrieve the indexed objects when there are no matches", func() {
  1382  					listOpts := &client.ListOptions{
  1383  						FieldSelector: fields.OneTermEqualSelector("spec.strategy.type", string(appsv1.RollingUpdateDeploymentStrategyType)),
  1384  					}
  1385  					list := &appsv1.DeploymentList{}
  1386  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1387  					Expect(list.Items).To(BeEmpty())
  1388  				})
  1389  
  1390  				It("no error when field selector uses two requirements", func() {
  1391  					listOpts := &client.ListOptions{
  1392  						FieldSelector: fields.AndSelectors(
  1393  							fields.OneTermEqualSelector("spec.replicas", "1"),
  1394  							fields.OneTermEqualSelector("spec.strategy.type", string(appsv1.RecreateDeploymentStrategyType)),
  1395  						)}
  1396  					list := &appsv1.DeploymentList{}
  1397  					Expect(cl.List(context.Background(), list, listOpts)).To(Succeed())
  1398  					Expect(list.Items).To(ConsistOf(*dep))
  1399  				})
  1400  			})
  1401  		})
  1402  	})
  1403  
  1404  	It("should set the ResourceVersion to 999 when adding an object to the tracker", func() {
  1405  		cl := NewClientBuilder().WithObjects(&corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "cm"}}).Build()
  1406  
  1407  		retrieved := &corev1.Secret{}
  1408  		Expect(cl.Get(context.Background(), types.NamespacedName{Name: "cm"}, retrieved)).To(Succeed())
  1409  
  1410  		reference := &corev1.Secret{
  1411  			ObjectMeta: metav1.ObjectMeta{
  1412  				Name:            "cm",
  1413  				ResourceVersion: "999",
  1414  			},
  1415  		}
  1416  		Expect(retrieved).To(Equal(reference))
  1417  	})
  1418  
  1419  	It("should be able to build with given tracker and get resource", func() {
  1420  		clientSet := fake.NewSimpleClientset(dep)
  1421  		cl := NewClientBuilder().WithRuntimeObjects(dep2).WithObjectTracker(clientSet.Tracker()).Build()
  1422  
  1423  		By("Getting a deployment")
  1424  		namespacedName := types.NamespacedName{
  1425  			Name:      "test-deployment",
  1426  			Namespace: "ns1",
  1427  		}
  1428  		obj := &appsv1.Deployment{}
  1429  		err := cl.Get(context.Background(), namespacedName, obj)
  1430  		Expect(err).ToNot(HaveOccurred())
  1431  		Expect(obj).To(Equal(dep))
  1432  
  1433  		By("Getting a deployment from clientSet")
  1434  		csDep2, err := clientSet.AppsV1().Deployments("ns1").Get(context.Background(), "test-deployment-2", metav1.GetOptions{})
  1435  		Expect(err).ToNot(HaveOccurred())
  1436  		Expect(csDep2).To(Equal(dep2))
  1437  
  1438  		By("Getting a new deployment")
  1439  		namespacedName3 := types.NamespacedName{
  1440  			Name:      "test-deployment-3",
  1441  			Namespace: "ns1",
  1442  		}
  1443  
  1444  		dep3 := &appsv1.Deployment{
  1445  			TypeMeta: metav1.TypeMeta{
  1446  				APIVersion: "apps/v1",
  1447  				Kind:       "Deployment",
  1448  			},
  1449  			ObjectMeta: metav1.ObjectMeta{
  1450  				Name:      "test-deployment-3",
  1451  				Namespace: "ns1",
  1452  				Labels: map[string]string{
  1453  					"test-label": "label-value",
  1454  				},
  1455  				ResourceVersion: trackerAddResourceVersion,
  1456  			},
  1457  		}
  1458  
  1459  		_, err = clientSet.AppsV1().Deployments("ns1").Create(context.Background(), dep3, metav1.CreateOptions{})
  1460  		Expect(err).ToNot(HaveOccurred())
  1461  
  1462  		obj = &appsv1.Deployment{}
  1463  		err = cl.Get(context.Background(), namespacedName3, obj)
  1464  		Expect(err).ToNot(HaveOccurred())
  1465  		Expect(obj).To(Equal(dep3))
  1466  	})
  1467  
  1468  	It("should not change the status of typed objects that have a status subresource on update", func() {
  1469  		obj := &corev1.Pod{
  1470  			ObjectMeta: metav1.ObjectMeta{
  1471  				Name: "pod",
  1472  			},
  1473  		}
  1474  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1475  
  1476  		obj.Status.Phase = "Running"
  1477  		Expect(cl.Update(context.Background(), obj)).To(Succeed())
  1478  
  1479  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1480  
  1481  		Expect(obj.Status).To(BeEquivalentTo(corev1.PodStatus{}))
  1482  	})
  1483  
  1484  	It("should return a conflict error when an incorrect RV is used on status update", func() {
  1485  		obj := &corev1.Node{
  1486  			ObjectMeta: metav1.ObjectMeta{
  1487  				Name:            "node",
  1488  				ResourceVersion: trackerAddResourceVersion,
  1489  			},
  1490  		}
  1491  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1492  
  1493  		obj.Status.Phase = corev1.NodeRunning
  1494  		obj.ResourceVersion = "invalid"
  1495  		err := cl.Status().Update(context.Background(), obj)
  1496  		Expect(apierrors.IsConflict(err)).To(BeTrue())
  1497  	})
  1498  
  1499  	It("should not change non-status field of typed objects that have a status subresource on status update", func() {
  1500  		obj := &corev1.Node{
  1501  			ObjectMeta: metav1.ObjectMeta{
  1502  				Name: "node",
  1503  			},
  1504  			Spec: corev1.NodeSpec{
  1505  				PodCIDR: "old-cidr",
  1506  			},
  1507  			Status: corev1.NodeStatus{
  1508  				NodeInfo: corev1.NodeSystemInfo{
  1509  					MachineID: "machine-id",
  1510  				},
  1511  			},
  1512  		}
  1513  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1514  		objOriginal := obj.DeepCopy()
  1515  
  1516  		obj.Spec.PodCIDR = cidrFromStatusUpdate
  1517  		obj.Annotations = map[string]string{
  1518  			"some-annotation-key": "some-annotation-value",
  1519  		}
  1520  		obj.Labels = map[string]string{
  1521  			"some-label-key": "some-label-value",
  1522  		}
  1523  
  1524  		obj.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1525  		Expect(cl.Status().Update(context.Background(), obj)).NotTo(HaveOccurred())
  1526  
  1527  		actual := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1528  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).NotTo(HaveOccurred())
  1529  
  1530  		objOriginal.APIVersion = actual.APIVersion
  1531  		objOriginal.Kind = actual.Kind
  1532  		objOriginal.ResourceVersion = actual.ResourceVersion
  1533  		objOriginal.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1534  		Expect(cmp.Diff(objOriginal, actual)).To(BeEmpty())
  1535  	})
  1536  
  1537  	It("should be able to update an object after updating an object's status", func() {
  1538  		obj := &corev1.Node{
  1539  			ObjectMeta: metav1.ObjectMeta{
  1540  				Name: "node",
  1541  			},
  1542  			Spec: corev1.NodeSpec{
  1543  				PodCIDR: "old-cidr",
  1544  			},
  1545  			Status: corev1.NodeStatus{
  1546  				NodeInfo: corev1.NodeSystemInfo{
  1547  					MachineID: "machine-id",
  1548  				},
  1549  			},
  1550  		}
  1551  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1552  		expectedObj := obj.DeepCopy()
  1553  
  1554  		obj.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1555  		Expect(cl.Status().Update(context.Background(), obj)).NotTo(HaveOccurred())
  1556  
  1557  		obj.Annotations = map[string]string{
  1558  			"some-annotation-key": "some",
  1559  		}
  1560  		expectedObj.Annotations = map[string]string{
  1561  			"some-annotation-key": "some",
  1562  		}
  1563  		Expect(cl.Update(context.Background(), obj)).NotTo(HaveOccurred())
  1564  
  1565  		actual := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1566  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).NotTo(HaveOccurred())
  1567  
  1568  		expectedObj.APIVersion = actual.APIVersion
  1569  		expectedObj.Kind = actual.Kind
  1570  		expectedObj.ResourceVersion = actual.ResourceVersion
  1571  		expectedObj.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1572  		Expect(cmp.Diff(expectedObj, actual)).To(BeEmpty())
  1573  	})
  1574  
  1575  	It("should be able to update an object's status after updating an object", func() {
  1576  		obj := &corev1.Node{
  1577  			ObjectMeta: metav1.ObjectMeta{
  1578  				Name: "node",
  1579  			},
  1580  			Spec: corev1.NodeSpec{
  1581  				PodCIDR: "old-cidr",
  1582  			},
  1583  			Status: corev1.NodeStatus{
  1584  				NodeInfo: corev1.NodeSystemInfo{
  1585  					MachineID: "machine-id",
  1586  				},
  1587  			},
  1588  		}
  1589  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1590  		expectedObj := obj.DeepCopy()
  1591  
  1592  		obj.Annotations = map[string]string{
  1593  			"some-annotation-key": "some",
  1594  		}
  1595  		expectedObj.Annotations = map[string]string{
  1596  			"some-annotation-key": "some",
  1597  		}
  1598  		Expect(cl.Update(context.Background(), obj)).NotTo(HaveOccurred())
  1599  
  1600  		obj.Spec.PodCIDR = cidrFromStatusUpdate
  1601  		obj.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1602  		Expect(cl.Status().Update(context.Background(), obj)).NotTo(HaveOccurred())
  1603  
  1604  		actual := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1605  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).NotTo(HaveOccurred())
  1606  
  1607  		expectedObj.APIVersion = actual.APIVersion
  1608  		expectedObj.Kind = actual.Kind
  1609  		expectedObj.ResourceVersion = actual.ResourceVersion
  1610  		expectedObj.Status.NodeInfo.MachineID = machineIDFromStatusUpdate
  1611  		Expect(cmp.Diff(expectedObj, actual)).To(BeEmpty())
  1612  	})
  1613  
  1614  	It("Should only override status fields of typed objects that have a status subresource on status update", func() {
  1615  		obj := &corev1.Node{
  1616  			ObjectMeta: metav1.ObjectMeta{
  1617  				Name: "node",
  1618  			},
  1619  			Spec: corev1.NodeSpec{
  1620  				PodCIDR: "old-cidr",
  1621  			},
  1622  			Status: corev1.NodeStatus{
  1623  				NodeInfo: corev1.NodeSystemInfo{
  1624  					MachineID: "machine-id",
  1625  				},
  1626  			},
  1627  		}
  1628  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1629  		objOriginal := obj.DeepCopy()
  1630  
  1631  		obj.Status.Phase = corev1.NodeRunning
  1632  		Expect(cl.Status().Update(context.Background(), obj)).NotTo(HaveOccurred())
  1633  
  1634  		actual := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1635  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).NotTo(HaveOccurred())
  1636  
  1637  		objOriginal.APIVersion = actual.APIVersion
  1638  		objOriginal.Kind = actual.Kind
  1639  		objOriginal.ResourceVersion = actual.ResourceVersion
  1640  		Expect(cmp.Diff(objOriginal, actual)).ToNot(BeEmpty())
  1641  		Expect(objOriginal.Status.NodeInfo.MachineID).To(Equal(actual.Status.NodeInfo.MachineID))
  1642  		Expect(objOriginal.Status.Phase).ToNot(Equal(actual.Status.Phase))
  1643  	})
  1644  
  1645  	It("should be able to change typed objects that have a scale subresource on patch", func() {
  1646  		obj := &appsv1.Deployment{
  1647  			ObjectMeta: metav1.ObjectMeta{
  1648  				Name: "deploy",
  1649  			},
  1650  		}
  1651  		cl := NewClientBuilder().WithObjects(obj).Build()
  1652  		objOriginal := obj.DeepCopy()
  1653  
  1654  		patch := []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, 2))
  1655  		Expect(cl.SubResource("scale").Patch(context.Background(), obj, client.RawPatch(types.MergePatchType, patch))).NotTo(HaveOccurred())
  1656  
  1657  		actual := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1658  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).To(Succeed())
  1659  
  1660  		objOriginal.APIVersion = actual.APIVersion
  1661  		objOriginal.Kind = actual.Kind
  1662  		objOriginal.ResourceVersion = actual.ResourceVersion
  1663  		objOriginal.Spec.Replicas = ptr.To(int32(2))
  1664  		Expect(cmp.Diff(objOriginal, actual)).To(BeEmpty())
  1665  	})
  1666  
  1667  	It("should not change the status of typed objects that have a status subresource on patch", func() {
  1668  		obj := &corev1.Pod{
  1669  			ObjectMeta: metav1.ObjectMeta{
  1670  				Name: "node",
  1671  			},
  1672  		}
  1673  		Expect(cl.Create(context.Background(), obj)).To(Succeed())
  1674  		original := obj.DeepCopy()
  1675  
  1676  		obj.Status.Phase = "Running"
  1677  		Expect(cl.Patch(context.Background(), obj, client.MergeFrom(original))).To(Succeed())
  1678  
  1679  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1680  
  1681  		Expect(obj.Status).To(BeEquivalentTo(corev1.PodStatus{}))
  1682  	})
  1683  
  1684  	It("should not change non-status field of typed objects that have a status subresource on status patch", func() {
  1685  		obj := &corev1.Node{
  1686  			ObjectMeta: metav1.ObjectMeta{
  1687  				Name: "node",
  1688  			},
  1689  			Spec: corev1.NodeSpec{
  1690  				PodCIDR: "old-cidr",
  1691  			},
  1692  		}
  1693  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1694  		objOriginal := obj.DeepCopy()
  1695  
  1696  		obj.Spec.PodCIDR = cidrFromStatusUpdate
  1697  		obj.Status.NodeInfo.MachineID = "machine-id"
  1698  		Expect(cl.Status().Patch(context.Background(), obj, client.MergeFrom(objOriginal))).NotTo(HaveOccurred())
  1699  
  1700  		actual := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: obj.Name}}
  1701  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(actual), actual)).NotTo(HaveOccurred())
  1702  
  1703  		objOriginal.APIVersion = actual.APIVersion
  1704  		objOriginal.Kind = actual.Kind
  1705  		objOriginal.ResourceVersion = actual.ResourceVersion
  1706  		objOriginal.Status.NodeInfo.MachineID = "machine-id"
  1707  		Expect(cmp.Diff(objOriginal, actual)).To(BeEmpty())
  1708  	})
  1709  
  1710  	It("should not change the status of unstructured objects that are configured to have a status subresource on update", func() {
  1711  		obj := &unstructured.Unstructured{}
  1712  		obj.SetAPIVersion("foo/v1")
  1713  		obj.SetKind("Foo")
  1714  		obj.SetName("a-foo")
  1715  
  1716  		err := unstructured.SetNestedField(obj.Object, map[string]any{"state": "old"}, "status")
  1717  		Expect(err).NotTo(HaveOccurred())
  1718  
  1719  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1720  
  1721  		err = unstructured.SetNestedField(obj.Object, map[string]any{"state": "new"}, "status")
  1722  		Expect(err).ToNot(HaveOccurred())
  1723  
  1724  		Expect(cl.Update(context.Background(), obj)).To(Succeed())
  1725  
  1726  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1727  
  1728  		Expect(obj.Object["status"]).To(BeEquivalentTo(map[string]any{"state": "old"}))
  1729  	})
  1730  
  1731  	It("should not change non-status fields of unstructured objects that are configured to have a status subresource on status update", func() {
  1732  		obj := &unstructured.Unstructured{}
  1733  		obj.SetAPIVersion("foo/v1")
  1734  		obj.SetKind("Foo")
  1735  		obj.SetName("a-foo")
  1736  
  1737  		err := unstructured.SetNestedField(obj.Object, "original", "spec")
  1738  		Expect(err).NotTo(HaveOccurred())
  1739  
  1740  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1741  
  1742  		err = unstructured.SetNestedField(obj.Object, "from-status-update", "spec")
  1743  		Expect(err).NotTo(HaveOccurred())
  1744  		err = unstructured.SetNestedField(obj.Object, map[string]any{"state": "new"}, "status")
  1745  		Expect(err).ToNot(HaveOccurred())
  1746  
  1747  		Expect(cl.Status().Update(context.Background(), obj)).To(Succeed())
  1748  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1749  
  1750  		Expect(obj.Object["status"]).To(BeEquivalentTo(map[string]any{"state": "new"}))
  1751  		Expect(obj.Object["spec"]).To(BeEquivalentTo("original"))
  1752  	})
  1753  
  1754  	It("should not change the status of known unstructured objects that have a status subresource on update", func() {
  1755  		obj := &corev1.Pod{
  1756  			ObjectMeta: metav1.ObjectMeta{
  1757  				Name: "pod",
  1758  			},
  1759  			Spec: corev1.PodSpec{
  1760  				RestartPolicy: corev1.RestartPolicyAlways,
  1761  			},
  1762  			Status: corev1.PodStatus{
  1763  				Phase: corev1.PodPending,
  1764  			},
  1765  		}
  1766  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1767  
  1768  		// update using unstructured
  1769  		u := &unstructured.Unstructured{}
  1770  		u.SetAPIVersion("v1")
  1771  		u.SetKind("Pod")
  1772  		u.SetName(obj.Name)
  1773  		err := cl.Get(context.Background(), client.ObjectKeyFromObject(u), u)
  1774  		Expect(err).NotTo(HaveOccurred())
  1775  
  1776  		err = unstructured.SetNestedField(u.Object, string(corev1.RestartPolicyNever), "spec", "restartPolicy")
  1777  		Expect(err).NotTo(HaveOccurred())
  1778  		err = unstructured.SetNestedField(u.Object, string(corev1.PodRunning), "status", "phase")
  1779  		Expect(err).NotTo(HaveOccurred())
  1780  
  1781  		Expect(cl.Update(context.Background(), u)).To(Succeed())
  1782  
  1783  		actual := &corev1.Pod{}
  1784  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), actual)).To(Succeed())
  1785  		obj.APIVersion = u.GetAPIVersion()
  1786  		obj.Kind = u.GetKind()
  1787  		obj.ResourceVersion = actual.ResourceVersion
  1788  		// only the spec mutation should persist
  1789  		obj.Spec.RestartPolicy = corev1.RestartPolicyNever
  1790  		Expect(cmp.Diff(obj, actual)).To(BeEmpty())
  1791  	})
  1792  
  1793  	It("should not change non-status field of known unstructured objects that have a status subresource on status update", func() {
  1794  		obj := &corev1.Pod{
  1795  			ObjectMeta: metav1.ObjectMeta{
  1796  				Name: "pod",
  1797  			},
  1798  			Spec: corev1.PodSpec{
  1799  				RestartPolicy: corev1.RestartPolicyAlways,
  1800  			},
  1801  			Status: corev1.PodStatus{
  1802  				Phase: corev1.PodPending,
  1803  			},
  1804  		}
  1805  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1806  
  1807  		// status update using unstructured
  1808  		u := &unstructured.Unstructured{}
  1809  		u.SetAPIVersion("v1")
  1810  		u.SetKind("Pod")
  1811  		u.SetName(obj.Name)
  1812  		err := cl.Get(context.Background(), client.ObjectKeyFromObject(u), u)
  1813  		Expect(err).NotTo(HaveOccurred())
  1814  
  1815  		err = unstructured.SetNestedField(u.Object, string(corev1.RestartPolicyNever), "spec", "restartPolicy")
  1816  		Expect(err).NotTo(HaveOccurred())
  1817  		err = unstructured.SetNestedField(u.Object, string(corev1.PodRunning), "status", "phase")
  1818  		Expect(err).NotTo(HaveOccurred())
  1819  
  1820  		Expect(cl.Status().Update(context.Background(), u)).To(Succeed())
  1821  
  1822  		actual := &corev1.Pod{}
  1823  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), actual)).To(Succeed())
  1824  		obj.ResourceVersion = actual.ResourceVersion
  1825  		// only the status mutation should persist
  1826  		obj.Status.Phase = corev1.PodRunning
  1827  		Expect(cmp.Diff(obj, actual)).To(BeEmpty())
  1828  	})
  1829  
  1830  	It("should not change the status of unstructured objects that are configured to have a status subresource on patch", func() {
  1831  		obj := &unstructured.Unstructured{}
  1832  		obj.SetAPIVersion("foo/v1")
  1833  		obj.SetKind("Foo")
  1834  		obj.SetName("a-foo")
  1835  		cl := NewClientBuilder().WithStatusSubresource(obj).Build()
  1836  
  1837  		Expect(cl.Create(context.Background(), obj)).To(Succeed())
  1838  		original := obj.DeepCopy()
  1839  
  1840  		err := unstructured.SetNestedField(obj.Object, map[string]interface{}{"count": int64(2)}, "status")
  1841  		Expect(err).ToNot(HaveOccurred())
  1842  		Expect(cl.Patch(context.Background(), obj, client.MergeFrom(original))).To(Succeed())
  1843  
  1844  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1845  
  1846  		Expect(obj.Object["status"]).To(BeNil())
  1847  
  1848  	})
  1849  
  1850  	It("should not change non-status fields of unstructured objects that are configured to have a status subresource on status patch", func() {
  1851  		obj := &unstructured.Unstructured{}
  1852  		obj.SetAPIVersion("foo/v1")
  1853  		obj.SetKind("Foo")
  1854  		obj.SetName("a-foo")
  1855  
  1856  		err := unstructured.SetNestedField(obj.Object, "original", "spec")
  1857  		Expect(err).NotTo(HaveOccurred())
  1858  
  1859  		cl := NewClientBuilder().WithStatusSubresource(obj).WithObjects(obj).Build()
  1860  		original := obj.DeepCopy()
  1861  
  1862  		err = unstructured.SetNestedField(obj.Object, "from-status-update", "spec")
  1863  		Expect(err).NotTo(HaveOccurred())
  1864  		err = unstructured.SetNestedField(obj.Object, map[string]any{"state": "new"}, "status")
  1865  		Expect(err).ToNot(HaveOccurred())
  1866  
  1867  		Expect(cl.Status().Patch(context.Background(), obj, client.MergeFrom(original))).To(Succeed())
  1868  		Expect(cl.Get(context.Background(), client.ObjectKeyFromObject(obj), obj)).To(Succeed())
  1869  
  1870  		Expect(obj.Object["status"]).To(BeEquivalentTo(map[string]any{"state": "new"}))
  1871  		Expect(obj.Object["spec"]).To(BeEquivalentTo("original"))
  1872  	})
  1873  
  1874  	It("should return not found on status update of resources that don't have a status subresource", func() {
  1875  		obj := &unstructured.Unstructured{}
  1876  		obj.SetAPIVersion("foo/v1")
  1877  		obj.SetKind("Foo")
  1878  		obj.SetName("a-foo")
  1879  
  1880  		cl := NewClientBuilder().WithObjects(obj).Build()
  1881  
  1882  		err := cl.Status().Update(context.Background(), obj)
  1883  		Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1884  	})
  1885  
  1886  	evictionTypes := []client.Object{
  1887  		&policyv1beta1.Eviction{},
  1888  		&policyv1.Eviction{},
  1889  	}
  1890  	for _, tp := range evictionTypes {
  1891  		It("should delete a pod through the eviction subresource", func() {
  1892  			pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
  1893  
  1894  			cl := NewClientBuilder().WithObjects(pod).Build()
  1895  
  1896  			err := cl.SubResource("eviction").Create(context.Background(), pod, tp)
  1897  			Expect(err).NotTo(HaveOccurred())
  1898  
  1899  			err = cl.Get(context.Background(), client.ObjectKeyFromObject(pod), pod)
  1900  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1901  		})
  1902  
  1903  		It("should return not found when attempting to evict a pod that doesn't exist", func() {
  1904  			cl := NewClientBuilder().Build()
  1905  
  1906  			pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
  1907  			err := cl.SubResource("eviction").Create(context.Background(), pod, tp)
  1908  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1909  		})
  1910  
  1911  		It("should return not found when attempting to evict something other than a pod", func() {
  1912  			ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
  1913  			cl := NewClientBuilder().WithObjects(ns).Build()
  1914  
  1915  			err := cl.SubResource("eviction").Create(context.Background(), ns, tp)
  1916  			Expect(apierrors.IsNotFound(err)).To(BeTrue())
  1917  		})
  1918  
  1919  		It("should return an error when using the wrong subresource", func() {
  1920  			cl := NewClientBuilder().Build()
  1921  
  1922  			err := cl.SubResource("eviction-subresource").Create(context.Background(), &corev1.Namespace{}, tp)
  1923  			Expect(err).To(HaveOccurred())
  1924  		})
  1925  	}
  1926  
  1927  	It("should error when creating an eviction with the wrong type", func() {
  1928  		cl := NewClientBuilder().Build()
  1929  		err := cl.SubResource("eviction").Create(context.Background(), &corev1.Pod{}, &corev1.Namespace{})
  1930  		Expect(apierrors.IsBadRequest(err)).To(BeTrue())
  1931  	})
  1932  
  1933  	It("should leave typemeta empty on typed get", func() {
  1934  		cl := NewClientBuilder().WithObjects(&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
  1935  			Namespace: "default",
  1936  			Name:      "foo",
  1937  		}}).Build()
  1938  
  1939  		var pod corev1.Pod
  1940  		Expect(cl.Get(context.Background(), client.ObjectKey{Namespace: "default", Name: "foo"}, &pod)).NotTo(HaveOccurred())
  1941  
  1942  		Expect(pod.TypeMeta).To(Equal(metav1.TypeMeta{}))
  1943  	})
  1944  
  1945  	It("should leave typemeta empty on typed list", func() {
  1946  		cl := NewClientBuilder().WithObjects(&corev1.Pod{ObjectMeta: metav1.ObjectMeta{
  1947  			Namespace: "default",
  1948  			Name:      "foo",
  1949  		}}).Build()
  1950  
  1951  		var podList corev1.PodList
  1952  		Expect(cl.List(context.Background(), &podList)).NotTo(HaveOccurred())
  1953  		Expect(podList.ListMeta).To(Equal(metav1.ListMeta{}))
  1954  		Expect(podList.Items[0].TypeMeta).To(Equal(metav1.TypeMeta{}))
  1955  	})
  1956  
  1957  	It("should be able to Get an object that has pointer fields for metadata", func() {
  1958  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  1959  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  1960  		scheme := runtime.NewScheme()
  1961  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  1962  
  1963  		cl := NewClientBuilder().
  1964  			WithScheme(scheme).
  1965  			WithObjects(&WithPointerMeta{ObjectMeta: &metav1.ObjectMeta{
  1966  				Name: "foo",
  1967  			}}).
  1968  			Build()
  1969  
  1970  		var object WithPointerMeta
  1971  		Expect(cl.Get(context.Background(), client.ObjectKey{Name: "foo"}, &object)).NotTo(HaveOccurred())
  1972  	})
  1973  
  1974  	It("should be able to List an object type that has pointer fields for metadata", func() {
  1975  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  1976  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  1977  		scheme := runtime.NewScheme()
  1978  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  1979  
  1980  		cl := NewClientBuilder().
  1981  			WithScheme(scheme).
  1982  			WithObjects(&WithPointerMeta{ObjectMeta: &metav1.ObjectMeta{
  1983  				Name: "foo",
  1984  			}}).
  1985  			Build()
  1986  
  1987  		var objectList WithPointerMetaList
  1988  		Expect(cl.List(context.Background(), &objectList)).NotTo(HaveOccurred())
  1989  		Expect(objectList.Items).To(HaveLen(1))
  1990  	})
  1991  
  1992  	It("should be able to List an object type that has pointer fields for metadata with no results", func() {
  1993  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  1994  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  1995  		scheme := runtime.NewScheme()
  1996  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  1997  
  1998  		cl := NewClientBuilder().
  1999  			WithScheme(scheme).
  2000  			Build()
  2001  
  2002  		var objectList WithPointerMetaList
  2003  		Expect(cl.List(context.Background(), &objectList)).NotTo(HaveOccurred())
  2004  		Expect(objectList.Items).To(BeEmpty())
  2005  	})
  2006  
  2007  	It("should be able to Patch an object type that has pointer fields for metadata", func() {
  2008  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  2009  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  2010  		scheme := runtime.NewScheme()
  2011  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  2012  
  2013  		obj := &WithPointerMeta{ObjectMeta: &metav1.ObjectMeta{
  2014  			Name: "foo",
  2015  		}}
  2016  		cl := NewClientBuilder().
  2017  			WithScheme(scheme).
  2018  			WithObjects(obj).
  2019  			Build()
  2020  
  2021  		original := obj.DeepCopy()
  2022  		obj.Labels = map[string]string{"foo": "bar"}
  2023  		Expect(cl.Patch(context.Background(), obj, client.MergeFrom(original))).NotTo(HaveOccurred())
  2024  
  2025  		Expect(cl.Get(context.Background(), client.ObjectKey{Name: "foo"}, obj)).NotTo(HaveOccurred())
  2026  		Expect(obj.Labels).To(Equal(map[string]string{"foo": "bar"}))
  2027  	})
  2028  
  2029  	It("should be able to Update an object type that has pointer fields for metadata", func() {
  2030  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  2031  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  2032  		scheme := runtime.NewScheme()
  2033  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  2034  
  2035  		obj := &WithPointerMeta{ObjectMeta: &metav1.ObjectMeta{
  2036  			Name: "foo",
  2037  		}}
  2038  		cl := NewClientBuilder().
  2039  			WithScheme(scheme).
  2040  			WithObjects(obj).
  2041  			Build()
  2042  
  2043  		Expect(cl.Get(context.Background(), client.ObjectKey{Name: "foo"}, obj)).NotTo(HaveOccurred())
  2044  
  2045  		obj.Labels = map[string]string{"foo": "bar"}
  2046  		Expect(cl.Update(context.Background(), obj)).NotTo(HaveOccurred())
  2047  
  2048  		Expect(cl.Get(context.Background(), client.ObjectKey{Name: "foo"}, obj)).NotTo(HaveOccurred())
  2049  		Expect(obj.Labels).To(Equal(map[string]string{"foo": "bar"}))
  2050  	})
  2051  
  2052  	It("should be able to Delete an object type that has pointer fields for metadata", func() {
  2053  		schemeBuilder := &scheme.Builder{GroupVersion: schema.GroupVersion{Group: "test", Version: "v1"}}
  2054  		schemeBuilder.Register(&WithPointerMeta{}, &WithPointerMetaList{})
  2055  		scheme := runtime.NewScheme()
  2056  		Expect(schemeBuilder.AddToScheme(scheme)).NotTo(HaveOccurred())
  2057  
  2058  		obj := &WithPointerMeta{ObjectMeta: &metav1.ObjectMeta{
  2059  			Name: "foo",
  2060  		}}
  2061  		cl := NewClientBuilder().
  2062  			WithScheme(scheme).
  2063  			WithObjects(obj).
  2064  			Build()
  2065  
  2066  		Expect(cl.Delete(context.Background(), obj)).NotTo(HaveOccurred())
  2067  
  2068  		err := cl.Get(context.Background(), client.ObjectKey{Name: "foo"}, obj)
  2069  		Expect(apierrors.IsNotFound(err)).To(BeTrue())
  2070  	})
  2071  })
  2072  
  2073  type WithPointerMetaList struct {
  2074  	*metav1.ListMeta
  2075  	*metav1.TypeMeta
  2076  	Items []*WithPointerMeta
  2077  }
  2078  
  2079  func (t *WithPointerMetaList) DeepCopy() *WithPointerMetaList {
  2080  	l := &WithPointerMetaList{
  2081  		ListMeta: t.ListMeta.DeepCopy(),
  2082  	}
  2083  	if t.TypeMeta != nil {
  2084  		l.TypeMeta = &metav1.TypeMeta{
  2085  			APIVersion: t.APIVersion,
  2086  			Kind:       t.Kind,
  2087  		}
  2088  	}
  2089  	for _, item := range t.Items {
  2090  		l.Items = append(l.Items, item.DeepCopy())
  2091  	}
  2092  
  2093  	return l
  2094  }
  2095  
  2096  func (t *WithPointerMetaList) DeepCopyObject() runtime.Object {
  2097  	return t.DeepCopy()
  2098  }
  2099  
  2100  type WithPointerMeta struct {
  2101  	*metav1.TypeMeta
  2102  	*metav1.ObjectMeta
  2103  }
  2104  
  2105  func (t *WithPointerMeta) DeepCopy() *WithPointerMeta {
  2106  	w := &WithPointerMeta{
  2107  		ObjectMeta: t.ObjectMeta.DeepCopy(),
  2108  	}
  2109  	if t.TypeMeta != nil {
  2110  		w.TypeMeta = &metav1.TypeMeta{
  2111  			APIVersion: t.APIVersion,
  2112  			Kind:       t.Kind,
  2113  		}
  2114  	}
  2115  
  2116  	return w
  2117  }
  2118  
  2119  func (t *WithPointerMeta) DeepCopyObject() runtime.Object {
  2120  	return t.DeepCopy()
  2121  }
  2122  
  2123  var _ = Describe("Fake client builder", func() {
  2124  	It("panics when an index with the same name and GroupVersionKind is registered twice", func() {
  2125  		// We need any realistic GroupVersionKind, the choice of apps/v1 Deployment is arbitrary.
  2126  		cb := NewClientBuilder().WithIndex(&appsv1.Deployment{},
  2127  			"test-name",
  2128  			func(client.Object) []string { return nil })
  2129  
  2130  		Expect(func() {
  2131  			cb.WithIndex(&appsv1.Deployment{},
  2132  				"test-name",
  2133  				func(client.Object) []string { return []string{"foo"} })
  2134  		}).To(Panic())
  2135  	})
  2136  
  2137  	It("should wrap the fake client with an interceptor when WithInterceptorFuncs is called", func() {
  2138  		var called bool
  2139  		cli := NewClientBuilder().WithInterceptorFuncs(interceptor.Funcs{
  2140  			Get: func(ctx context.Context, client client.WithWatch, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
  2141  				called = true
  2142  				return nil
  2143  			},
  2144  		}).Build()
  2145  		err := cli.Get(context.Background(), client.ObjectKey{}, &corev1.Pod{})
  2146  		Expect(err).NotTo(HaveOccurred())
  2147  		Expect(called).To(BeTrue())
  2148  	})
  2149  })
  2150  

View as plain text