Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pluginpresets): deletion protection for plugin preset #809

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions docs/user-guides/plugin/plugin-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ In case the _PluginPreset_ is updated all of the Plugin instances that are manag
Changes that are done directly on a _Plugin_ which was created from a _PluginPreset_ will be overwritten immediately by the _PluginPreset_ Controller. All changes must be performed on the _PluginPreset_ itself.
If a _Plugin_ already existed with the same name as the _PluginPreset_ would create, this _Plugin_ will be ignored in following reconciliations.

To prevent accidental deletion of the _Plugin_, the _PluginPreset_ has the annotation `greenhouse.sap/prevent-deletion`. To delete the pluginPreset along with the plugins, you must first remove the annotation.

## Example _PluginPreset_

```yaml
Expand Down
34 changes: 32 additions & 2 deletions pkg/admission/pluginpreset_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (

// Webhook for the PluginPreset custom resource.

const preventDeletionAnnotation = "greenhouse.sap/prevent-deletion"

func SetupPluginPresetWebhookWithManager(mgr ctrl.Manager) error {
return setupWebhook(mgr,
&greenhousev1alpha1.PluginPreset{},
Expand All @@ -33,7 +35,20 @@ func SetupPluginPresetWebhookWithManager(mgr ctrl.Manager) error {

//+kubebuilder:webhook:path=/mutate-greenhouse-sap-v1alpha1-pluginpreset,mutating=true,failurePolicy=fail,sideEffects=None,groups=greenhouse.sap,resources=pluginpresets,verbs=create;update,versions=v1alpha1,name=mpluginpreset.kb.io,admissionReviewVersions=v1

func DefaultPluginPreset(_ context.Context, _ client.Client, _ runtime.Object) error {
func DefaultPluginPreset(_ context.Context, _ client.Client, o runtime.Object) error {
pluginPreset, ok := o.(*greenhousev1alpha1.PluginPreset)
if !ok {
return nil
}

// prevent deletion on plugin preset creation
if pluginPreset.Annotations == nil {
pluginPreset.Annotations = map[string]string{}
}
if pluginPreset.CreationTimestamp.IsZero() {
pluginPreset.Annotations[preventDeletionAnnotation] = "true"
}

return nil
}

Expand Down Expand Up @@ -109,6 +124,21 @@ func ValidateUpdatePluginPreset(ctx context.Context, c client.Client, oldObj, cu
return nil, nil
}

func ValidateDeletePluginPreset(_ context.Context, _ client.Client, _ runtime.Object) (admission.Warnings, error) {
func ValidateDeletePluginPreset(_ context.Context, _ client.Client, obj runtime.Object) (admission.Warnings, error) {
pluginPreset, ok := obj.(*greenhousev1alpha1.PluginPreset)
if !ok {
return nil, nil
}

var allErrs field.ErrorList
if _, ok := pluginPreset.Annotations[preventDeletionAnnotation]; ok {
allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("annotation").Child(preventDeletionAnnotation),
pluginPreset.Annotations, "Plugin preset has prevent deletion annotation"))
}

if len(allErrs) > 0 {
return nil, apierrors.NewInvalid(pluginPreset.GroupVersionKind().GroupKind(), pluginPreset.Name, allErrs)
}

return nil, nil
}
37 changes: 37 additions & 0 deletions pkg/admission/pluginpreset_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,44 @@ var _ = Describe("PluginPreset Admission Tests", Ordered, func() {
Expect(err.Error()).
To(ContainSubstring("field is immutable"), "the error must reflect the field is immutable")

_, err = clientutil.CreateOrPatch(test.Ctx, test.K8sClient, cut, func() error {
delete(cut.Annotations, preventDeletionAnnotation)
return nil
})
Expect(err).
ToNot(HaveOccurred())
Expect(test.K8sClient.Delete(test.Ctx, cut)).
To(Succeed(), "there must be no error deleting the PluginPreset")
})

It("should reject delete operation when PluginPreset has prevent deletion annotation", func() {
pluginPreset := &greenhousev1alpha1.PluginPreset{
ObjectMeta: metav1.ObjectMeta{
Name: pluginPresetUpdate,
Namespace: test.TestNamespace,
Annotations: map[string]string{
preventDeletionAnnotation: "true",
},
},
Spec: greenhousev1alpha1.PluginPresetSpec{
Plugin: greenhousev1alpha1.PluginSpec{
PluginDefinition: pluginPresetDefinition,
},
ClusterSelector: metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
},
}

err := test.K8sClient.Create(test.Ctx, pluginPreset)
Expect(err).ToNot(HaveOccurred())

err = test.K8sClient.Delete(test.Ctx, pluginPreset)
Expect(err).To(HaveOccurred())

pluginPreset.Annotations = map[string]string{}
err = test.K8sClient.Update(test.Ctx, pluginPreset)
Expect(err).ToNot(HaveOccurred())

err = test.K8sClient.Delete(test.Ctx, pluginPreset)
Expect(err).ToNot(HaveOccurred())
})
})
9 changes: 9 additions & 0 deletions pkg/controllers/plugin/pluginpreset_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ var _ = Describe("PluginPreset Controller Lifecycle", Ordered, func() {
Expect(err).NotTo(HaveOccurred(), "failed to update Plugin")

By("deleting the PluginPreset")
testPluginPreset.Annotations = map[string]string{}
err = test.K8sClient.Update(test.Ctx, testPluginPreset)
Expect(err).ToNot(HaveOccurred())
Expect(test.K8sClient.Delete(test.Ctx, testPluginPreset)).Should(Succeed(), "failed to delete test PluginPreset")
})

Expand Down Expand Up @@ -195,6 +198,12 @@ var _ = Describe("PluginPreset Controller Lifecycle", Ordered, func() {
}))

By("removing plugin preset")
Eventually(func(g Gomega) {
err := test.K8sClient.Get(test.Ctx, client.ObjectKeyFromObject(pluginPreset), pluginPreset)
g.Expect(err).ShouldNot(HaveOccurred(), "unexpected error getting PluginPreset")
pluginPreset.Annotations = map[string]string{}
Expect(test.K8sClient.Update(test.Ctx, pluginPreset)).ToNot(HaveOccurred())
}).Should(Succeed(), "failed to update PluginPreset")
Expect(test.K8sClient.Delete(test.Ctx, pluginPreset)).ToNot(HaveOccurred())
test.EventuallyDeleted(test.Ctx, test.K8sClient, pluginPreset)
})
Expand Down
Loading