From 7fa47ee3c50a6393cc5fbb5356263beb09f81313 Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Wed, 29 Jan 2025 18:39:04 +0000 Subject: [PATCH 1/2] Update object upon deletion If an object is deleted and the API responds with a resource, update the deleted object. This causes the object to be updated with the deletion timestamp. Signed-off-by: Kevin McDermott --- pkg/client/client_test.go | 32 ++++++++++++++++++++++++++++++++ pkg/client/dryrun_test.go | 6 ++++-- pkg/client/typed_client.go | 2 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index ff014e7321..b96fab0f60 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -50,6 +50,7 @@ import ( "sigs.k8s.io/controller-runtime/examples/crd/pkg" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) func deleteDeployment(ctx context.Context, dep *appsv1.Deployment, ns string) { @@ -1638,6 +1639,7 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC nodeName := node.Name err = cl.Delete(context.TODO(), node) Expect(err).NotTo(HaveOccurred()) + Expect(node.ObjectMeta.DeletionTimestamp).To(BeNil()) By("validating the Node no longer exists") _, err = clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) @@ -1654,6 +1656,36 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC Expect(err).To(HaveOccurred()) }) + It("should update the resource when deleting if it receives a response", func() { + cl, err := client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + Expect(cl).NotTo(BeNil()) + + By("initially creating a Node") + node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("adding a finalizer we prevent the node from being deleted immediately") + controllerutil.AddFinalizer(node, "example.com/test") + node, err = clientset.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("deleting the Node") + nodeName := node.Name + err = cl.Delete(context.TODO(), node) + Expect(err).NotTo(HaveOccurred()) + Expect(node.ObjectMeta.DeletionTimestamp).NotTo(BeNil()) + + By("removing the finalizer") + controllerutil.RemoveFinalizer(node, "example.com/test") + _, err = clientset.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("validating the Node no longer exists") + _, err = clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) + Expect(err).To(HaveOccurred()) + }) + PIt("should fail if the object doesn't have meta", func() { }) diff --git a/pkg/client/dryrun_test.go b/pkg/client/dryrun_test.go index 0d370e0576..630b2c6721 100644 --- a/pkg/client/dryrun_test.go +++ b/pkg/client/dryrun_test.go @@ -182,7 +182,8 @@ var _ = Describe("DryRunClient", func() { }) It("should not delete objects", func() { - Expect(getClient().Delete(ctx, dep)).NotTo(HaveOccurred()) + changedDep := dep.DeepCopy() + Expect(getClient().Delete(ctx, changedDep)).NotTo(HaveOccurred()) actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -193,7 +194,8 @@ var _ = Describe("DryRunClient", func() { It("should not delete objects with opts", func() { opts := &client.DeleteOptions{DryRun: []string{"Bye", "Pippa"}} - Expect(getClient().Delete(ctx, dep, opts)).NotTo(HaveOccurred()) + changedDep := dep.DeepCopy() + Expect(getClient().Delete(ctx, changedDep, opts)).NotTo(HaveOccurred()) actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{}) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/client/typed_client.go b/pkg/client/typed_client.go index 92afd9a9c2..f721e1ed8f 100644 --- a/pkg/client/typed_client.go +++ b/pkg/client/typed_client.go @@ -85,7 +85,7 @@ func (c *typedClient) Delete(ctx context.Context, obj Object, opts ...DeleteOpti Name(o.GetName()). Body(deleteOpts.AsDeleteOptions()). Do(ctx). - Error() + Into(obj) } // DeleteAllOf implements client.Client. From 4c4e252b122eb8739aad9040f078ff15053012d0 Mon Sep 17 00:00:00 2001 From: Kevin McDermott Date: Sun, 9 Feb 2025 13:16:42 +0000 Subject: [PATCH 2/2] Update the unstructured client to return the values. Signed-off-by: Kevin McDermott --- pkg/client/client_test.go | 43 ++++++++++++++++++++++++++++++- pkg/client/unstructured_client.go | 2 +- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index b96fab0f60..5a7f4e8617 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -1672,7 +1672,7 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC By("deleting the Node") nodeName := node.Name - err = cl.Delete(context.TODO(), node) + err = cl.Delete(ctx, node) Expect(err).NotTo(HaveOccurred()) Expect(node.ObjectMeta.DeletionTimestamp).NotTo(BeNil()) @@ -1845,7 +1845,48 @@ U5wwSivyi7vmegHKmblOzNVKA5qPO8zWzqBC _, err = clientset.AppsV1().Deployments(ns).Get(ctx, dep2Name, metav1.GetOptions{}) Expect(err).To(HaveOccurred()) }) + + It("should update the resource when deleting if it receives a response", func() { + cl, err := client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + Expect(cl).NotTo(BeNil()) + + By("initially creating a Node") + node, err := clientset.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("adding a finalizer we prevent the node from being deleted immediately") + controllerutil.AddFinalizer(node, "example.com/test") + node, err = clientset.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("deleting the Node") + nodeName := node.Name + u := &unstructured.Unstructured{} + Expect(scheme.Convert(node, u, nil)).To(Succeed()) + u.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "", + Kind: "Node", + Version: "v1", + }) + err = cl.Delete(ctx, u) + Expect(err).NotTo(HaveOccurred()) + + accessor, err := meta.Accessor(u) + Expect(err).NotTo(HaveOccurred()) + Expect(accessor.GetDeletionTimestamp()).NotTo(BeNil()) + + By("removing the finalizer") + controllerutil.RemoveFinalizer(u, "example.com/test") + err = cl.Delete(ctx, u) + Expect(err).NotTo(HaveOccurred()) + + By("validating the Node no longer exists") + _, err = clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + }) }) + Context("with metadata objects", func() { It("should delete an existing object from a go struct", func() { cl, err := client.New(cfg, client.Options{}) diff --git a/pkg/client/unstructured_client.go b/pkg/client/unstructured_client.go index 0d96951780..bb75961ea8 100644 --- a/pkg/client/unstructured_client.go +++ b/pkg/client/unstructured_client.go @@ -111,7 +111,7 @@ func (uc *unstructuredClient) Delete(ctx context.Context, obj Object, opts ...De Name(o.GetName()). Body(deleteOpts.AsDeleteOptions()). Do(ctx). - Error() + Into(obj) } // DeleteAllOf implements client.Client.