diff --git a/pkg/client/fake/client.go b/pkg/client/fake/client.go index bb0913e021..ded8a60d33 100644 --- a/pkg/client/fake/client.go +++ b/pkg/client/fake/client.go @@ -23,10 +23,12 @@ import ( "fmt" "strconv" "strings" + "sync" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" utilrand "k8s.io/apimachinery/pkg/util/rand" @@ -46,8 +48,9 @@ type versionedTracker struct { } type fakeClient struct { - tracker versionedTracker - scheme *runtime.Scheme + tracker versionedTracker + scheme *runtime.Scheme + schemeWriteLock sync.Mutex } var _ client.WithWatch = &fakeClient{} @@ -314,6 +317,14 @@ func (c *fakeClient) List(ctx context.Context, obj client.ObjectList, opts ...cl gvk.Kind = gvk.Kind[:len(gvk.Kind)-4] } + if _, isUnstructuredList := obj.(*unstructured.UnstructuredList); isUnstructuredList && !c.scheme.Recognizes(gvk) { + // We need tor register the ListKind with UnstructuredList: + // https://github.com/kubernetes/kubernetes/blob/7b2776b89fb1be28d4e9203bdeec079be903c103/staging/src/k8s.io/client-go/dynamic/fake/simple.go#L44-L51 + c.schemeWriteLock.Lock() + c.scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(gvk.Kind+"List"), &unstructured.UnstructuredList{}) + c.schemeWriteLock.Unlock() + } + listOpts := client.ListOptions{} listOpts.ApplyOptions(opts) diff --git a/pkg/client/fake/client_test.go b/pkg/client/fake/client_test.go index f49d2ed6f6..6cf1e9b2ad 100644 --- a/pkg/client/fake/client_test.go +++ b/pkg/client/fake/client_test.go @@ -141,6 +141,132 @@ var _ = Describe("Fake client", func() { Expect(list.Items).To(HaveLen(2)) }) + It("should be able to Create an unregistered type using unstructured", func() { + item := &unstructured.Unstructured{} + item.SetAPIVersion("custom/v1") + item.SetKind("Image") + item.SetName("my-item") + err := cl.Create(context.Background(), item) + Expect(err).To(BeNil()) + }) + + It("should be able to Get an unregisted type using unstructured", func() { + By("Creating an object of an unregistered type") + item := &unstructured.Unstructured{} + item.SetAPIVersion("custom/v2") + item.SetKind("Image") + item.SetName("my-item") + err := cl.Create(context.Background(), item) + Expect(err).To(BeNil()) + + By("Getting and the object") + item = &unstructured.Unstructured{} + item.SetAPIVersion("custom/v2") + item.SetKind("Image") + item.SetName("my-item") + err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item) + Expect(err).To(BeNil()) + }) + + It("should be able to List an unregistered type using unstructured", func() { + list := &unstructured.UnstructuredList{} + list.SetAPIVersion("custom/v3") + list.SetKind("ImageList") + err := cl.List(context.Background(), list) + Expect(err).To(BeNil()) + }) + + It("should be able to List an unregistered type using unstructured", func() { + list := &unstructured.UnstructuredList{} + list.SetAPIVersion("custom/v4") + list.SetKind("Image") + err := cl.List(context.Background(), list) + Expect(err).To(BeNil()) + }) + + It("should be able to Update an unregistered type using unstructured", func() { + By("Creating an object of an unregistered type") + item := &unstructured.Unstructured{} + item.SetAPIVersion("custom/v5") + item.SetKind("Image") + item.SetName("my-item") + err := cl.Create(context.Background(), item) + Expect(err).To(BeNil()) + + By("Updating the object") + err = unstructured.SetNestedField(item.Object, int64(2), "spec", "replicas") + Expect(err).To(BeNil()) + err = cl.Update(context.Background(), item) + Expect(err).To(BeNil()) + + By("Getting the object") + item = &unstructured.Unstructured{} + item.SetAPIVersion("custom/v5") + item.SetKind("Image") + item.SetName("my-item") + err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item) + Expect(err).To(BeNil()) + + By("Inspecting the object") + value, found, err := unstructured.NestedInt64(item.Object, "spec", "replicas") + Expect(err).To(BeNil()) + Expect(found).To(BeTrue()) + Expect(value).To(Equal(int64(2))) + }) + + It("should be able to Patch an unregistered type using unstructured", func() { + By("Creating an object of an unregistered type") + item := &unstructured.Unstructured{} + item.SetAPIVersion("custom/v6") + item.SetKind("Image") + item.SetName("my-item") + err := cl.Create(context.Background(), item) + Expect(err).To(BeNil()) + + By("Updating the object") + original := item.DeepCopy() + err = unstructured.SetNestedField(item.Object, int64(2), "spec", "replicas") + Expect(err).To(BeNil()) + err = cl.Patch(context.Background(), item, client.MergeFrom(original)) + Expect(err).To(BeNil()) + + By("Getting the object") + item = &unstructured.Unstructured{} + item.SetAPIVersion("custom/v6") + item.SetKind("Image") + item.SetName("my-item") + err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item) + Expect(err).To(BeNil()) + + By("Inspecting the object") + value, found, err := unstructured.NestedInt64(item.Object, "spec", "replicas") + Expect(err).To(BeNil()) + Expect(found).To(BeTrue()) + Expect(value).To(Equal(int64(2))) + }) + + It("should be able to Delete an unregistered type using unstructured", func() { + By("Creating an object of an unregistered type") + item := &unstructured.Unstructured{} + item.SetAPIVersion("custom/v7") + item.SetKind("Image") + item.SetName("my-item") + err := cl.Create(context.Background(), item) + Expect(err).To(BeNil()) + + By("Deleting the object") + err = cl.Delete(context.Background(), item) + Expect(err).To(BeNil()) + + By("Getting the object") + item = &unstructured.Unstructured{} + item.SetAPIVersion("custom/v7") + item.SetKind("Image") + item.SetName("my-item") + err = cl.Get(context.Background(), client.ObjectKeyFromObject(item), item) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }) + It("should support filtering by labels and their values", func() { By("Listing deployments with a particular label and value") list := &appsv1.DeploymentList{}