Skip to content

Commit 81104fa

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

File tree

2 files changed

+71
-19
lines changed

2 files changed

+71
-19
lines changed

pkg/skaffold/deploy/helm/helm.go

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"io"
2626
"os"
2727
"path/filepath"
28+
"slices"
2829
"sort"
2930
"strings"
3031
sync2 "sync"
@@ -450,30 +451,62 @@ func (h *Deployer) Cleanup(ctx context.Context, out io.Writer, dryRun bool, _ ma
450451
"DeployerType": "helm",
451452
})
452453

453-
var errMsgs []string
454+
dependencyGraph, err := NewDependencyGraph(h.Releases)
455+
if err != nil {
456+
return fmt.Errorf("unable to create dependency graph: %w", err)
457+
}
458+
459+
levelByLevelReleases, err := dependencyGraph.GetReleasesByLevel()
460+
if err != nil {
461+
return fmt.Errorf("unable to get releases by level: %w", err)
462+
}
463+
464+
releaseNameToRelease := make(map[string]latest.HelmRelease)
454465
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-
}
466+
releaseNameToRelease[r.Name] = r
467+
}
459468

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")
469+
levels := make([]int, 0, len(levelByLevelReleases))
470+
for level := range levelByLevelReleases {
471+
levels = append(levels, level)
472+
}
473+
// Sort levels in ascending order
474+
sort.Ints(levels)
475+
476+
var errMsgs []string
477+
for _, level := range slices.Backward(levels) {
478+
releases := levelByLevelReleases[level]
479+
if len(levelByLevelReleases) > 1 {
480+
olog.Entry(ctx).Infof("Cleaning up level %d/%d releases (%d releases)", level+1, len(levelByLevelReleases), len(releases))
467481
} else {
468-
args = append(args, "delete")
482+
olog.Entry(ctx).Infof("Cleaning up releases (%d releases)", len(releases))
469483
}
470-
args = append(args, releaseName)
471484

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())
485+
for _, name := range slices.Backward(releases) {
486+
release := releaseNameToRelease[name]
487+
releaseName, err := util.ExpandEnvTemplateOrFail(release.Name, nil)
488+
if err != nil {
489+
return fmt.Errorf("cannot parse the release name template: %w", err)
490+
}
491+
492+
namespace, err := helm.ReleaseNamespace(h.namespace, release)
493+
if err != nil {
494+
return err
495+
}
496+
args := []string{}
497+
if dryRun {
498+
args = append(args, "get", "manifest")
499+
} else {
500+
args = append(args, "delete")
501+
}
502+
args = append(args, releaseName)
503+
504+
if namespace != "" {
505+
args = append(args, "--namespace", namespace)
506+
}
507+
if err := helm.Exec(ctx, h, out, false, nil, args...); err != nil {
508+
errMsgs = append(errMsgs, err.Error())
509+
}
477510
}
478511
}
479512

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)