Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2516,6 +2516,43 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC
Expect(hasDep).To(BeTrue())
})

It("should fetch unstructured collection of objects when setting a non-list kind", func(ctx SpecContext) {
// While it is not ideal to omit the List suffix it can easily happen.
// As the client is using TrimSuffix(gvk.Kind,"List") this also works.
// As it works it is part of our API and we should test it accordingly.
By("create an initial object")
_, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
Expect(err).NotTo(HaveOccurred())

cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())

By("listing all objects of that type in the cluster")
deps := &unstructured.UnstructuredList{}
deps.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
})
err = cl.List(ctx, deps)
Expect(err).NotTo(HaveOccurred())

Expect(deps.Items).NotTo(BeEmpty())
hasDep := false
for _, item := range deps.Items {
Expect(item.GroupVersionKind()).To(Equal(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
}))
if item.GetName() == dep.Name && item.GetNamespace() == dep.Namespace {
hasDep = true
break
}
}
Expect(hasDep).To(BeTrue())
})

It("should fetch unstructured collection of objects, even if scheme is empty", func(ctx SpecContext) {
By("create an initial object")
_, err := clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
Expand Down
6 changes: 6 additions & 0 deletions pkg/client/fake/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,12 @@ func (c *fakeClient) addToSchemeIfUnknownAndUnstructuredOrPartial(obj runtime.Ob
return err
}

if isUnstructuredList || isPartialList {
if !strings.HasSuffix(gvk.Kind, "List") {
gvk.Kind += "List"
}
}

if !c.scheme.Recognizes(gvk) {
c.scheme.AddKnownTypeWithName(gvk, obj)
}
Expand Down
34 changes: 34 additions & 0 deletions pkg/client/fake/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2939,6 +2939,40 @@ var _ = Describe("Fake client", func() {
Expect(result.Object["spec"]).To(Equal(map[string]any{"other": "data"}))
})

It("supports server-side apply of a custom resource via Apply method after List with a non-list kind", func(ctx SpecContext) {
cl := NewClientBuilder().Build()

// Previously this List call lead to addToSchemeIfUnknownAndUnstructuredOrPartial adding FakeResource as UnstructuredList to the scheme.
// This broke the subsequent SSA call as it was trying to create a new FakeResource object as an UnstructuredList.
// After a fix this List call leads to addToSchemeIfUnknownAndUnstructuredOrPartial adding FakeResourceList as UnstructuredList to the
// scheme even if the UnstructuredList here is missing the List suffix.
objList := &unstructured.UnstructuredList{}
objList.SetAPIVersion("custom/v1")
objList.SetKind("FakeResource")
Expect(cl.List(ctx, objList)).To(Succeed())

obj := &unstructured.Unstructured{}
obj.SetAPIVersion("custom/v1")
obj.SetKind("FakeResource")
obj.SetName("foo")
result := obj.DeepCopy()

Expect(unstructured.SetNestedField(obj.Object, map[string]any{"some": "data"}, "spec")).To(Succeed())

applyConfig := client.ApplyConfigurationFromUnstructured(obj)
Expect(cl.Apply(ctx, applyConfig, &client.ApplyOptions{FieldManager: "test-manager"})).To(Succeed())

Expect(cl.Get(ctx, client.ObjectKeyFromObject(result), result)).To(Succeed())
Expect(result.Object["spec"]).To(Equal(map[string]any{"some": "data"}))

Expect(unstructured.SetNestedField(obj.Object, map[string]any{"other": "data"}, "spec")).To(Succeed())
applyConfig2 := client.ApplyConfigurationFromUnstructured(obj)
Expect(cl.Apply(ctx, applyConfig2, &client.ApplyOptions{FieldManager: "test-manager"})).To(Succeed())

Expect(cl.Get(ctx, client.ObjectKeyFromObject(result), result)).To(Succeed())
Expect(result.Object["spec"]).To(Equal(map[string]any{"other": "data"}))
})

It("sets the fieldManager in create, patch and update", func(ctx SpecContext) {
owner := "test-owner"
cl := client.WithFieldOwner(
Expand Down