Skip to content

Commit

Permalink
Implement helm.sh/resource-deletion-policy annotation to override del…
Browse files Browse the repository at this point in the history
…etion cascade per resource

Signed-off-by: Danil Grigorev <danil.grigorev@suse.com>
  • Loading branch information
Danil-Grigorev committed Apr 2, 2024
1 parent 833bbfa commit 769742a
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
10 changes: 9 additions & 1 deletion pkg/kube/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,15 @@ func rdelete(c *Client, resources ResourceList, propagation metav1.DeletionPropa
mtx := sync.Mutex{}
err := perform(resources, func(info *resource.Info) error {
c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind)
err := deleteResource(info, propagation)
propagationPolicy := propagation
if annotations, err := metadataAccessor.Annotations(info.Object); err != nil {
c.Log("Unable to get annotations on %q, err: %s", info.Name, err)
errs = append(errs, err)
} else if annotations != nil && annotations[ResourceDeletionPolicyAnno] != "" {
propagationPolicy = selectDeletionPolicy(annotations[ResourceDeletionPolicyAnno], propagation)
}

err := deleteResource(info, propagationPolicy)
if err == nil || apierrors.IsNotFound(err) {
if err != nil {
c.Log("Ignoring delete failure for %q %s: %v", info.Name, info.Mapping.GroupVersionKind, err)
Expand Down
87 changes: 87 additions & 0 deletions pkg/kube/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,93 @@ func TestUpdate(t *testing.T) {
}
}

func TestDelete(t *testing.T) {
listA := newPodList("starfish", "otter", "squid", "jellyfish")
listA.Items[0].Annotations = map[string]string{
ResourceDeletionPolicyAnno: "foreground",
}
listA.Items[1].Annotations = map[string]string{
ResourceDeletionPolicyAnno: "orphan",
}
listA.Items[2].Annotations = map[string]string{
ResourceDeletionPolicyAnno: "background",
}
listA.Items[3].Annotations = map[string]string{
ResourceDeletionPolicyAnno: "oops",
}

var actions []string

c := newTestClient(t)
c.Factory.(*cmdtesting.TestFactory).UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
b, err := io.ReadAll(req.Body)
if err != nil {
t.Fatal(err)
}
req.Body.Close()
actions = append(actions, p+":"+m)
t.Logf("got request %s %s", p, m)
switch {
case p == "/namespaces/default/pods/jellyfish" && m == "DELETE" && string(b) == "{\"propagationPolicy\":\"Background\"}\n":
return newResponse(200, &listA.Items[3])
case p == "/namespaces/default/pods/squid" && m == "DELETE" && string(b) == "{\"propagationPolicy\":\"Background\"}\n":
return newResponse(200, &listA.Items[2])
case p == "/namespaces/default/pods/otter" && m == "DELETE" && string(b) == "{\"propagationPolicy\":\"Orphan\"}\n":
return newResponse(200, &listA.Items[1])
case p == "/namespaces/default/pods/starfish" && m == "DELETE" && string(b) == "{\"propagationPolicy\":\"Foreground\"}\n":
return newResponse(200, &listA.Items[0])
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
first, err := c.Build(objBody(&listA), false)
if err != nil {
t.Fatal(err)
}

result, errs := c.Delete(first)
if len(errs) > 0 {
t.Fatal(errs)
}

if len(result.Created) != 0 {
t.Errorf("expected 0 resource created, got %d", len(result.Created))
}
if len(result.Updated) != 0 {
t.Errorf("expected 0 resource updated, got %d", len(result.Updated))
}
if len(result.Deleted) != 4 {
t.Errorf("expected 4 resource deleted, got %d", len(result.Deleted))
}

expectedActions := []string{
"/namespaces/default/pods/squid:DELETE",
"/namespaces/default/pods/starfish:DELETE",
"/namespaces/default/pods/otter:DELETE",
"/namespaces/default/pods/jellyfish:DELETE",
}
if len(expectedActions) != len(actions) {
t.Fatalf("unexpected number of requests, expected %d, got %d", len(expectedActions), len(actions))
}
for _, v := range expectedActions {
found := false
for _, action := range actions {
if action == v {
found = true
}
}

if !found {
t.Errorf("expected %s request got %#v", v, actions)
}
}
}

func TestBuild(t *testing.T) {
tests := []struct {
name string
Expand Down
40 changes: 40 additions & 0 deletions pkg/kube/resource_delete_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package kube // import "helm.sh/helm/v3/pkg/kube"

import (
"strings"

v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const ResourceDeletionPolicyAnno = "helm.sh/resource-deletion-policy"

// selectDeletionPolicy allows to select an override deleteion policy per resource,
// based on ResourceDeletionPolicyAnno.
func selectDeletionPolicy(policyAnnotation string, defualt v1.DeletionPropagation) v1.DeletionPropagation {
switch policyAnnotation {
case strings.ToLower(string(v1.DeletePropagationBackground)):
return v1.DeletePropagationBackground
case strings.ToLower(string(v1.DeletePropagationForeground)):
return v1.DeletePropagationForeground
case strings.ToLower(string(v1.DeletePropagationOrphan)):
return v1.DeletePropagationOrphan
default:
return defualt
}
}

0 comments on commit 769742a

Please sign in to comment.