diff --git a/integrationtests/cli/apply/apply_online_test.go b/integrationtests/cli/apply/apply_online_test.go index d12654b82e..a8d1d7025c 100644 --- a/integrationtests/cli/apply/apply_online_test.go +++ b/integrationtests/cli/apply/apply_online_test.go @@ -5,6 +5,8 @@ package apply import ( + "time" + "go.uber.org/mock/gomock" . "github.com/onsi/ginkgo/v2" @@ -116,4 +118,70 @@ data: }) + When("a bundle to be created already exists and is marked for deletion", func() { + BeforeEach(func() { + ts := metav1.NewTime(time.Now()) + name = "labels_update" + dirs = []string{cli.AssetsPath + "labels_update"} + //bundle in the cluster + oldBundle = &fleet.Bundle{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "foo", + Name: "test_labels", + DeletionTimestamp: &ts, // as long as it's non-nil + }, + } + // bundle in the cm.yaml file; some values are autofilled in the implementation (this is why there are not only the labels) + newBundle = &fleet.Bundle{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "new": "fleet-label2", + }, + Namespace: "foo", + Name: "test_labels", + }, + Spec: fleet.BundleSpec{ + Resources: []fleet.BundleResource{ + { + Name: "cm.yaml", + Content: `apiVersion: v1 +kind: ConfigMap +metadata: + name: cm3 +data: + test: "value23" +`, + }, + }, + Targets: []fleet.BundleTarget{ + { + Name: "default", + ClusterGroup: "default", + }, + }, + }, + } + }) + + It("does not update the bundle", func() { + // no expected call to update nor updateStatus, as the existing bundle is being deleted + err := fleetApplyOnline(getter, name, dirs, options) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("is being deleted")) + }) + + It("does not update an OCI bundle", func() { + // no expected call to update nor updateStatus, as the existing bundle is being deleted + bkp := options.OCIRegistry.Reference + options.OCIRegistry.Reference = "oci://foo" // non-empty + + defer func() { options.OCIRegistry.Reference = bkp }() + + err := fleetApplyOnline(getter, name, dirs, options) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("is being deleted")) + }) + + }) + }) diff --git a/internal/cmd/cli/apply/apply.go b/internal/cmd/cli/apply/apply.go index 17a13f1d1a..f44cb22dc3 100644 --- a/internal/cmd/cli/apply/apply.go +++ b/internal/cmd/cli/apply/apply.go @@ -247,6 +247,13 @@ func Dir(ctx context.Context, client Getter, name, baseDir string, opts *Options return err } + // We need to exit early if the bundle is being deleted + if tmp, err := c.Fleet.Bundle().Get(bundle.Namespace, bundle.Name, metav1.GetOptions{}); err == nil { + if tmp.DeletionTimestamp != nil { + return fmt.Errorf("the bundle %q is being deleted, cannot create during a delete operation", bundle.Name) + } + } + h, data, err := helmvalues.ExtractValues(bundle) if err != nil { return err @@ -324,6 +331,11 @@ func save(c *client.Client, bundle *fleet.Bundle) (*fleet.Bundle, error) { } logrus.Infof("created (bundle): %s/%s", bundle.Namespace, bundle.Name) } else { + // We cannot update a bundle that is going to be deleted, our update would be lost + if obj.DeletionTimestamp != nil { + return bundle, fmt.Errorf("the bundle %q is being deleted", bundle.Name) + } + obj.Spec = bundle.Spec obj.Annotations = bundle.Annotations obj.Labels = bundle.Labels @@ -391,6 +403,10 @@ func saveOCIBundle(ctx context.Context, c *client.Client, bundle *fleet.Bundle, } logrus.Infof("createOrUpdate (oci secret): %s/%s", obj.Namespace, obj.Name) } else { + if obj.DeletionTimestamp != nil { + return bundle, fmt.Errorf("the bundle %q is being deleted", bundle.Name) + } + obj.Spec = bundle.Spec obj.Annotations = bundle.Annotations obj.Labels = bundle.Labels