Skip to content

Commit c7baead

Browse files
committed
fix(helm): implement ordered cleanup for Helm releases based on dependency levels
1 parent f460879 commit c7baead

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

pkg/skaffold/deploy/helm/helm.go

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,7 @@ func (h *Deployer) Deploy(ctx context.Context, out io.Writer, builds []graph.Art
295295
releaseNameToRelease[r.Name] = r
296296
}
297297

298-
levels := make([]int, 0, len(levelByLevelReleases))
299-
for level := range levelByLevelReleases {
300-
levels = append(levels, level)
301-
}
302-
// Sort levels in ascending order
303-
sort.Ints(levels)
304-
305-
// Process each level in order
306-
for _, level := range levels {
298+
for level := 0; level < len(levelByLevelReleases); level++ {
307299
releases := levelByLevelReleases[level]
308300
if len(levelByLevelReleases) > 1 {
309301
olog.Entry(ctx).Infof("Installing level %d/%d releases (%d releases)", level+1, len(levelByLevelReleases), len(releases))
@@ -450,30 +442,41 @@ func (h *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ ma
450442
"DeployerType": "helm",
451443
})
452444

453-
var errMsgs []string
445+
dependencyGraph, err := NewDependencyGraph(h.Releases)
446+
if err != nil {
447+
return fmt.Errorf("unable to create dependency graph: %w", err)
448+
}
449+
450+
levelByLevelReleases, err := dependencyGraph.GetReleasesByLevel()
451+
if err != nil {
452+
return fmt.Errorf("unable to get releases by level: %w", err)
453+
}
454+
455+
releaseNameToRelease := make(map[string]latest.HelmRelease)
454456
for _, r := range h.Releases {
455-
releaseName, err := util.ExpandEnvTemplateOrFail(r.Name, nil)
456-
if err != nil {
457-
return fmt.Errorf("cannot parse the release name template: %w", err)
458-
}
457+
releaseNameToRelease[r.Name] = r
458+
}
459459

460-
namespace, err := helm.ReleaseNamespace(h.namespace, r)
461-
if err != nil {
462-
return err
463-
}
464-
args := []string{}
465-
if dryRun {
466-
args = append(args, "get", "manifest")
460+
var errMsgs []string
461+
for level := len(levelByLevelReleases) - 1; level >= 0; level-- {
462+
releases := levelByLevelReleases[level]
463+
if len(levelByLevelReleases) > 1 {
464+
olog.Entry(ctx).Infof("Cleaning up level %d/%d releases (%d releases)", level+1, len(levelByLevelReleases), len(releases))
467465
} else {
468-
args = append(args, "delete")
466+
olog.Entry(ctx).Infof("Cleaning up releases (%d releases)", len(releases))
469467
}
470-
args = append(args, releaseName)
471468

472-
if namespace != "" {
473-
args = append(args, "--namespace", namespace)
474-
}
475-
if err := helm.Exec(ctx, h, out, false, nil, args...); err != nil {
476-
errMsgs = append(errMsgs, err.Error())
469+
for _, name := range releases {
470+
olog.Entry(ctx).Infof("Cleaning up release: %s", name)
471+
release := releaseNameToRelease[name]
472+
releaseName, err := util.ExpandEnvTemplateOrFail(release.Name, nil)
473+
if err != nil {
474+
return fmt.Errorf("cannot parse the release name template: %w", err)
475+
}
476+
477+
if err := h.undeployRelease(ctx, out, releaseName, release, dryRun); err != nil {
478+
errMsgs = append(errMsgs, err.Error())
479+
}
477480
}
478481
}
479482

@@ -616,6 +619,25 @@ func (h *Deployer) deployRelease(ctx context.Context, out io.Writer, releaseName
616619
return b, artifacts, nil
617620
}
618621

622+
func (h *Deployer) undeployRelease(ctx context.Context, out io.Writer, releaseName string, r latest.HelmRelease, dryRun bool) error {
623+
namespace, err := helm.ReleaseNamespace(h.namespace, r)
624+
if err != nil {
625+
return err
626+
}
627+
args := []string{}
628+
if dryRun {
629+
args = append(args, "get", "manifest")
630+
} else {
631+
args = append(args, "delete")
632+
}
633+
args = append(args, releaseName)
634+
635+
if namespace != "" {
636+
args = append(args, "--namespace", namespace)
637+
}
638+
return helm.Exec(ctx, h, out, false, nil, args...)
639+
}
640+
619641
func getPostRendererFlag(flags []string) []string {
620642
for i, ele := range flags {
621643
if strings.HasPrefix(ele, "--post-renderer") {

pkg/skaffold/deploy/helm/helm_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ var testDeployPreservingOrderWithDependsOnConfig = latest.LegacyHelmDeploy{
9898
* level 1: B, D
9999
* level 2: A, E
100100
*/
101+
/** Expected order of deletion:
102+
* level 2: E, A
103+
* level 1: D, B
104+
* level 0: F, C
105+
*/
101106
Releases: []latest.HelmRelease{{
102107
Name: "A",
103108
ChartPath: "examples/test",
@@ -1282,6 +1287,20 @@ func TestHelmCleanup(t *testing.T) {
12821287
namespace: kubectl.TestNamespace,
12831288
builds: testBuilds,
12841289
},
1290+
{
1291+
description: "helm3 ordered cleanup success",
1292+
commands: testutil.
1293+
CmdRunWithOutput("helm version --client", version31).
1294+
AndRun("helm --kube-context kubecontext delete E --namespace testNamespace --kubeconfig kubeconfig").
1295+
AndRun("helm --kube-context kubecontext delete A --namespace testNamespace --kubeconfig kubeconfig").
1296+
AndRun("helm --kube-context kubecontext delete D --namespace testNamespace --kubeconfig kubeconfig").
1297+
AndRun("helm --kube-context kubecontext delete B --namespace testNamespace --kubeconfig kubeconfig").
1298+
AndRun("helm --kube-context kubecontext delete F --namespace testNamespace --kubeconfig kubeconfig").
1299+
AndRun("helm --kube-context kubecontext delete C --namespace testNamespace --kubeconfig kubeconfig"),
1300+
helm: testDeployPreservingOrderWithDependsOnConfig,
1301+
namespace: kubectl.TestNamespace,
1302+
builds: testBuilds,
1303+
},
12851304
}
12861305
for _, test := range tests {
12871306
testutil.Run(t, test.description, func(t *testutil.T) {

0 commit comments

Comments
 (0)