Feat add cascade support (#860)

* feat: add cascade support for helm v3.12.0

Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero
2023-05-15 13:49:33 +08:00
committed by GitHub
parent 8e036e19dc
commit 00dace9b63
19 changed files with 188 additions and 20 deletions
+18
View File
@@ -39,6 +39,24 @@ func (st *HelmState) appendPostRenderFlags(flags []string, release *ReleaseSpec,
return flags
}
// append post-renderer flags to helm flags
func (st *HelmState) appendCascadeFlags(flags []string, helm helmexec.Interface, release *ReleaseSpec, cascade string) []string {
// see https://github.com/helm/helm/releases/tag/v3.12.0
if !helm.IsVersionAtLeast("3.12.0") {
return flags
}
switch {
// postRenderer arg comes from cmd flag.
case release.Cascade != nil && *release.Cascade != "":
flags = append(flags, "--cascade", *release.Cascade)
case cascade != "":
flags = append(flags, "--cascade", cascade)
case st.HelmDefaults.Cascade != nil && *st.HelmDefaults.Cascade != "":
flags = append(flags, "--cascade", *st.HelmDefaults.Cascade)
}
return flags
}
type Chartify struct {
Opts *chartify.ChartifyOpts
Clean func()
+75
View File
@@ -0,0 +1,75 @@
package state
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testutil"
)
func TestAppendCascadeFlags(t *testing.T) {
type args struct {
flags []string
release *ReleaseSpec
cascade string
helm helmexec.Interface
helmSpec HelmSpec
expected []string
}
tests := []struct {
name string
args args
}{
{
name: "no cascade when helm less than 3.11.0",
args: args{
flags: []string{},
release: &ReleaseSpec{},
cascade: "background",
helm: testutil.NewVersionHelmExec("3.11.0"),
expected: []string{},
},
},
{
name: "cascade from release",
args: args{
flags: []string{},
release: &ReleaseSpec{Cascade: &[]string{"background", "background"}[0]},
cascade: "",
helm: testutil.NewVersionHelmExec("3.12.0"),
expected: []string{"--cascade", "background"},
},
},
{
name: "cascade from cmd flag",
args: args{
flags: []string{},
release: &ReleaseSpec{},
cascade: "background",
helm: testutil.NewVersionHelmExec("3.12.0"),
expected: []string{"--cascade", "background"},
},
},
{
name: "cascade from helm defaults",
args: args{
flags: []string{},
release: &ReleaseSpec{},
helmSpec: HelmSpec{Cascade: &[]string{"background", "background"}[0]},
cascade: "",
helm: testutil.NewVersionHelmExec("3.12.0"),
expected: []string{"--cascade", "background"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st := &HelmState{}
st.HelmDefaults = tt.args.helmSpec
got := st.appendCascadeFlags(tt.args.flags, tt.args.helm, tt.args.release, tt.args.cascade)
require.Equalf(t, tt.args.expected, got, "appendCascadeFlags() = %v, want %v", got, tt.args.expected)
})
}
}
+12 -3
View File
@@ -187,6 +187,8 @@ type HelmSpec struct {
ReuseValues bool `yaml:"reuseValues"`
// Propagate '--post-renderer' to helmv3 template and helm install
PostRenderer *string `yaml:"postRenderer,omitempty"`
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade *string `yaml:"cascade,omitempty"`
TLS bool `yaml:"tls"`
TLSCACert string `yaml:"tlsCACert,omitempty"`
@@ -360,6 +362,9 @@ type ReleaseSpec struct {
// Propagate '--post-renderer' to helmv3 template and helm install
PostRenderer *string `yaml:"postRenderer,omitempty"`
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade *string `yaml:"cascade,omitempty"`
// Inherit is used to inherit a release template from a release or another release template
Inherit Inherits `yaml:"inherit,omitempty"`
}
@@ -767,7 +772,7 @@ func ReleaseToID(r *ReleaseSpec) string {
}
// DeleteReleasesForSync deletes releases that are marked for deletion
func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, helm helmexec.Interface, workerLimit int) []error {
func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, helm helmexec.Interface, workerLimit int, cascade string) []error {
errs := []error{}
releases := st.Releases
@@ -801,7 +806,9 @@ func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, h
if release.Namespace != "" {
args = append(args, "--namespace", release.Namespace)
}
deletionFlags := st.appendConnectionFlags(args, release)
args = st.appendConnectionFlags(args, release)
deletionFlags := st.appendCascadeFlags(args, helm, release, cascade)
m.Lock()
start := time.Now()
if _, err := st.triggerReleaseEvent("preuninstall", nil, release, "sync"); err != nil {
@@ -2031,15 +2038,17 @@ func (st *HelmState) ReleaseStatuses(helm helmexec.Interface, workerLimit int) [
}
// DeleteReleases wrapper for executing helm delete on the releases
func (st *HelmState) DeleteReleases(affectedReleases *AffectedReleases, helm helmexec.Interface, concurrency int, purge bool) []error {
func (st *HelmState) DeleteReleases(affectedReleases *AffectedReleases, helm helmexec.Interface, concurrency int, purge bool, cascade string) []error {
return st.scatterGatherReleases(helm, concurrency, func(release ReleaseSpec, workerIndex int) error {
st.ApplyOverrides(&release)
flags := make([]string, 0)
flags = st.appendConnectionFlags(flags, &release)
flags = st.appendCascadeFlags(flags, helm, &release, cascade)
if release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}
context := st.createHelmContext(&release, workerIndex)
start := time.Now()
+1 -1
View File
@@ -2647,7 +2647,7 @@ func TestHelmState_Delete(t *testing.T) {
helm.Lists[exectest.ListKey{Filter: "^" + name + "$", Flags: tt.flags}] = name
}
affectedReleases := AffectedReleases{}
errs := state.DeleteReleases(&affectedReleases, helm, 1, tt.purge)
errs := state.DeleteReleases(&affectedReleases, helm, 1, tt.purge, "")
if errs != nil {
if !tt.wantErr || len(affectedReleases.Failed) != 1 || affectedReleases.Failed[0].Name != release.Name {
t.Errorf("DeleteReleases() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
+6 -6
View File
@@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) {
run(testcase{
subject: "baseline",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
want: "foo-values-76d4857b56",
want: "foo-values-6cbf8f5f9f",
})
run(testcase{
subject: "different bytes content",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
data: []byte(`{"k":"v"}`),
want: "foo-values-55d59fb487",
want: "foo-values-6cb9d4f956",
})
run(testcase{
subject: "different map content",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
data: map[string]interface{}{"k": "v"},
want: "foo-values-7c8d99f9f7",
want: "foo-values-5bdffb5f4b",
})
run(testcase{
subject: "different chart",
release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"},
want: "foo-values-7467f76549",
want: "foo-values-6595bd68c6",
})
run(testcase{
subject: "different name",
release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"},
want: "bar-values-dbdf465b7",
want: "bar-values-75698946b",
})
run(testcase{
subject: "specific ns",
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"},
want: "myns-foo-values-6d4874d757",
want: "myns-foo-values-5bf477bbfb",
})
for id, n := range ids {