From df03e6eedaaaa0da090a80f01cc59c0083cc1a29 Mon Sep 17 00:00:00 2001 From: Daishan Peng Date: Wed, 14 Feb 2024 14:13:29 -0700 Subject: [PATCH] Fix: add logic to update volume reclaim policy to delete Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng Signed-off-by: Daishan Peng --- .../apigroups/acorn/volumes/policyswitcher.go | 49 +++++++++++++++++++ .../apigroups/acorn/volumes/storage.go | 9 ++-- .../apigroups/acorn/volumes/translator.go | 30 ++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 pkg/server/registry/apigroups/acorn/volumes/policyswitcher.go diff --git a/pkg/server/registry/apigroups/acorn/volumes/policyswitcher.go b/pkg/server/registry/apigroups/acorn/volumes/policyswitcher.go new file mode 100644 index 000000000..74d27a3ef --- /dev/null +++ b/pkg/server/registry/apigroups/acorn/volumes/policyswitcher.go @@ -0,0 +1,49 @@ +package volumes + +import ( + "context" + + "github.com/acorn-io/mink/pkg/strategy" + "github.com/acorn-io/mink/pkg/strategy/remote" + "github.com/acorn-io/mink/pkg/types" + v1 "k8s.io/api/core/v1" +) + +type PolicySwitcherStrategy struct { + strategy strategy.CompleteStrategy + translator *Translator + remote *remote.Remote +} + +// NewPolicySwitcherStrategy returns a new policy switcher strategy that switch persistvolume policy from retain to delete when the volume is being deleted. +// This makes sure the actual storage resource is deleted when the volume is deleted. +func NewPolicySwitcherStrategy(s strategy.CompleteStrategy, t *Translator, r *remote.Remote) strategy.Deleter { + return &PolicySwitcherStrategy{ + translator: t, + strategy: s, + remote: r, + } +} + +func (p *PolicySwitcherStrategy) Delete(ctx context.Context, obj types.Object) (types.Object, error) { + pvName, err := p.translator.FromVolumeToPVName(ctx, obj.GetNamespace(), obj.GetName()) + if err != nil { + return nil, err + } + pvObj, err := p.remote.Get(ctx, "", pvName) + if err != nil { + return nil, err + } + pv := pvObj.(*v1.PersistentVolume) + pv.Spec.PersistentVolumeReclaimPolicy = v1.PersistentVolumeReclaimDelete + _, err = p.remote.Update(ctx, pv) + if err != nil { + return nil, err + } + + return p.strategy.Delete(ctx, obj) +} + +func (p *PolicySwitcherStrategy) Get(ctx context.Context, namespace, name string) (types.Object, error) { + return p.strategy.Get(ctx, namespace, name) +} diff --git a/pkg/server/registry/apigroups/acorn/volumes/storage.go b/pkg/server/registry/apigroups/acorn/volumes/storage.go index 23a7e131e..befbe14da 100644 --- a/pkg/server/registry/apigroups/acorn/volumes/storage.go +++ b/pkg/server/registry/apigroups/acorn/volumes/storage.go @@ -13,15 +13,18 @@ import ( ) func NewStorage(c kclient.WithWatch) rest.Storage { - translated := translation.NewTranslationStrategy(&Translator{ + r := remote.NewRemote(&corev1.PersistentVolume{}, c) + t := &Translator{ c: c, - }, remote.NewRemote(&corev1.PersistentVolume{}, c)) + } + translated := translation.NewTranslationStrategy(t, r) remoteResource := publicname.NewStrategy(translated) + policySwitcher := NewPolicySwitcherStrategy(remoteResource, t, r) return stores.NewBuilder(c.Scheme(), &apiv1.Volume{}). WithGet(remoteResource). WithList(remoteResource). - WithDelete(remoteResource). + WithDelete(policySwitcher). WithWatch(remoteResource). WithTableConverter(tables.VolumeConverter). Build() diff --git a/pkg/server/registry/apigroups/acorn/volumes/translator.go b/pkg/server/registry/apigroups/acorn/volumes/translator.go index 5eeff359f..f9c841c59 100644 --- a/pkg/server/registry/apigroups/acorn/volumes/translator.go +++ b/pkg/server/registry/apigroups/acorn/volumes/translator.go @@ -21,6 +21,36 @@ type Translator struct { c kclient.Client } +func (t *Translator) FromVolumeToPVName(ctx context.Context, namespace, name string) (string, error) { + i := strings.LastIndex(name, ".") + // If there is not a period, or string ends with period, parse it not as an alias + if i == -1 || i+1 >= len(name) { + return name, nil + } + + // parse it of the form . + prefix := name[:i] + volumeName := name[i+1:] + + pvs := &corev1.PersistentVolumeList{} + err := t.c.List(ctx, pvs, &kclient.ListOptions{ + LabelSelector: klabels.SelectorFromSet(map[string]string{ + labels.AcornAppName: prefix, + labels.AcornAppNamespace: namespace, + labels.AcornVolumeName: volumeName, + }), + }) + if err != nil { + return "", err + } + + if len(pvs.Items) == 1 { + return pvs.Items[0].Name, nil + } + + return name, nil +} + func (t *Translator) FromPublicName(ctx context.Context, namespace, name string) (string, string, error) { i := strings.LastIndex(name, ".") // If there is not a period, or string ends with period, parse it not as an alias