Skip to content

Commit

Permalink
Feat add cascade support (helmfile#860)
Browse files Browse the repository at this point in the history
* feat: add cascade support for helm v3.12.0

Signed-off-by: yxxhero <[email protected]>
  • Loading branch information
yxxhero authored May 15, 2023
1 parent 8e036e1 commit 00dace9
Show file tree
Hide file tree
Showing 19 changed files with 188 additions and 20 deletions.
1 change: 1 addition & 0 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&applyOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reuse-values"`)
f.BoolVar(&applyOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`)
f.StringVar(&applyOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringVar(&applyOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")

return cmd
}
1 change: 1 addition & 0 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewDeleteCmd(globalCfg *config.GlobalImpl) *cobra.Command {

f := cmd.Flags()
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec")
f.StringVar(&deleteOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
f.IntVar(&deleteOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
f.BoolVar(&deleteOptions.Purge, "purge", false, "purge releases i.e. free release names and histories")
f.BoolVar(&deleteOptions.SkipCharts, "skip-charts", false, "don't prepare charts when deleting releases")
Expand Down
1 change: 1 addition & 0 deletions cmd/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func NewDestroyCmd(globalCfg *config.GlobalImpl) *cobra.Command {

f := cmd.Flags()
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec")
f.StringVar(&destroyOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
f.IntVar(&destroyOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
f.BoolVar(&destroyOptions.SkipCharts, "skip-charts", false, "don't prepare charts when destroying releases")

Expand Down
1 change: 1 addition & 0 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&syncOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reuse-values"`)
f.BoolVar(&syncOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`)
f.StringVar(&syncOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringVar(&syncOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")

return cmd
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ require (
github.com/a8m/envsubst v1.3.0 // indirect
github.com/aws/aws-sdk-go v1.44.122 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fujiwara/tfstate-lookup v1.1.1 // indirect
Expand Down
6 changes: 3 additions & 3 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -1441,7 +1441,7 @@ Do you really want to apply?

subst.Releases = rs

return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency())
return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency(), c.Cascade())
}))

if len(deletionErrs) > 0 {
Expand Down Expand Up @@ -1560,7 +1560,7 @@ Do you really want to delete?

if len(releasesToDelete) > 0 {
_, deletionErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toDelete, Reverse: true, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
return subst.DeleteReleases(&affectedReleases, helm, c.Concurrency(), purge)
return subst.DeleteReleases(&affectedReleases, helm, c.Concurrency(), purge, c.Cascade())
}))

if len(deletionErrs) > 0 {
Expand Down Expand Up @@ -1832,7 +1832,7 @@ Do you really want to sync?

subst.Releases = rs

return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency())
return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency(), c.Cascade())
}))

if len(deletionErrs) > 0 {
Expand Down
11 changes: 8 additions & 3 deletions pkg/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func expectNoCallsToHelmVersion(app *App) {
}

app.helms = map[helmKey]helmexec.Interface{
createHelmKey(app.OverrideHelmBinary, app.OverrideKubeContext): &versionOnlyHelmExec{isHelm3: true},
createHelmKey(app.OverrideHelmBinary, app.OverrideKubeContext): testutil.NewV3HelmExec(true),
}
}

Expand Down Expand Up @@ -2185,8 +2185,9 @@ func (c configImpl) KubeVersion() string {
}

type applyConfig struct {
args string
values []string
args string
cascade string
values []string

// TODO: Remove this function once Helmfile v0.x
retainValuesFiles bool
Expand Down Expand Up @@ -2230,6 +2231,10 @@ func (a applyConfig) Args() string {
return a.args
}

func (a applyConfig) Cascade() string {
return a.cascade
}

func (a applyConfig) Wait() bool {
return a.wait
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type ReposConfigProvider interface {
type ApplyConfigProvider interface {
Args() string
PostRenderer() string
Cascade() string

Values() []string
Set() []string
Expand Down Expand Up @@ -88,6 +89,7 @@ type ApplyConfigProvider interface {
type SyncConfigProvider interface {
Args() string
PostRenderer() string
Cascade() string

Values() []string
Set() []string
Expand Down Expand Up @@ -144,6 +146,7 @@ type DiffConfigProvider interface {
// TODO: Remove this function once Helmfile v0.x
type DeleteConfigProvider interface {
Args() string
Cascade() string

Purge() bool
SkipDeps() bool
Expand All @@ -156,6 +159,7 @@ type DeleteConfigProvider interface {

type DestroyConfigProvider interface {
Args() string
Cascade() string

SkipDeps() bool
SkipCharts() bool
Expand Down
5 changes: 5 additions & 0 deletions pkg/app/destroy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func listFlags(namespace, kubeContext string) string {

type destroyConfig struct {
args string
cascade string
concurrency int
interactive bool
skipDeps bool
Expand All @@ -46,6 +47,10 @@ func (d destroyConfig) Args() string {
return d.args
}

func (d destroyConfig) Cascade() string {
return d.cascade
}

func (d destroyConfig) SkipCharts() bool {
return d.skipCharts
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/config/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type ApplyOptions struct {
ResetValues bool
// Propagate '--post-renderer' to helmv3 template and helm install
PostRenderer string
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade string
}

// NewApply creates a new Apply
Expand Down Expand Up @@ -212,3 +214,8 @@ func (a *ApplyImpl) ResetValues() bool {
func (a *ApplyImpl) PostRenderer() string {
return a.ApplyOptions.PostRenderer
}

// Cascade returns cascade flag
func (a *ApplyImpl) Cascade() string {
return a.ApplyOptions.Cascade
}
7 changes: 7 additions & 0 deletions pkg/config/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ type DeleteOptions struct {
Purge bool
// SkipCharts makes Delete skip `withPreparedCharts`
SkipCharts bool
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade string
}

// NewDeleteOptions creates a new Apply
Expand Down Expand Up @@ -44,3 +46,8 @@ func (c *DeleteImpl) Purge() bool {
func (c *DeleteImpl) SkipCharts() bool {
return c.DeleteOptions.SkipCharts
}

// Cascade returns cascade flag
func (c *DeleteImpl) Cascade() string {
return c.DeleteOptions.Cascade
}
7 changes: 7 additions & 0 deletions pkg/config/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ type DestroyOptions struct {
Concurrency int
// SkipCharts makes Destroy skip `withPreparedCharts`
SkipCharts bool
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade string
}

// NewDestroyOptions creates a new Apply
Expand Down Expand Up @@ -36,3 +38,8 @@ func (c *DestroyImpl) Concurrency() int {
func (c *DestroyImpl) SkipCharts() bool {
return c.DestroyOptions.SkipCharts
}

// Cascade returns cascade flag
func (c *DestroyImpl) Cascade() string {
return c.DestroyOptions.Cascade
}
7 changes: 7 additions & 0 deletions pkg/config/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type SyncOptions struct {
ResetValues bool
// Propagate '--post-renderer' to helmv3 template and helm install
PostRenderer string
// Cascade '--cascade' to helmv3 delete, available values: background, foreground, or orphan, default: background
Cascade string
}

// NewSyncOptions creates a new Apply
Expand Down Expand Up @@ -118,3 +120,8 @@ func (t *SyncImpl) ResetValues() bool {
func (t *SyncImpl) PostRenderer() string {
return t.SyncOptions.PostRenderer
}

// Cascade returns cascade flag
func (t *SyncImpl) Cascade() string {
return t.SyncOptions.Cascade
}
18 changes: 18 additions & 0 deletions pkg/state/helmx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
75 changes: 75 additions & 0 deletions pkg/state/helmx_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
15 changes: 12 additions & 3 deletions pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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"`
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion pkg/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 00dace9

Please sign in to comment.