mirror of
https://github.com/helmfile/helmfile.git
synced 2026-10-18 19:38:37 +02:00
feat: update strategy for reinstall (#2019)
* feat: Add updateStrategy option in the state file with 'reinstall'/'reinstallIfForbidden' choices to uninstall and apply the specific release(s) (if forbidden to update) Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Fix unit tests related to the new updateStrategy feature Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Fix unit tests related to the new updateStrategy feature Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Resolve linter issue due to cognitive complexity Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Updated index.md to describe the possible values of updateStrategy Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Add validation of updateStrategy parameter and unit test Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Updated unit test Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Removed 'reinstall' update strategy option to only have reinstallIfForbidden, cleanup of pre-sync changes, adapted unit tests Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Display affected releases that were reinstalled Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Make sure to add --wait when deleting a release to be reinstalled due to reinstallIfForbidden Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Apply suggestions from Copilot code review Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> --------- Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>
This commit is contained in:
@@ -415,4 +415,28 @@ releases:
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("show diff on changed selected release with reinstall", func(t *testing.T) {
|
||||
check(t, testcase{
|
||||
helmfile: `
|
||||
releases:
|
||||
- name: a
|
||||
chart: incubator/raw
|
||||
namespace: default
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: b
|
||||
chart: incubator/raw
|
||||
namespace: default
|
||||
`,
|
||||
selectors: []string{"name=a"},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^a$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
diffed: []exectest.Release{
|
||||
{Name: "a", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values"}},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+183
-1
@@ -220,6 +220,97 @@ releases:
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateStrategyParamValidation(t *testing.T) {
|
||||
cases := []struct {
|
||||
files map[string]string
|
||||
updateStrategy string
|
||||
isValid bool
|
||||
}{
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`},
|
||||
"reinstallIfForbidden",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`},
|
||||
"reinstallIfForbidden",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy:
|
||||
`},
|
||||
"",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: foo
|
||||
`},
|
||||
"foo",
|
||||
false},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstal
|
||||
`},
|
||||
"reinstal",
|
||||
false},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstall1
|
||||
`},
|
||||
"reinstall1",
|
||||
false},
|
||||
}
|
||||
|
||||
for idx, c := range cases {
|
||||
fs := testhelper.NewTestFs(c.files)
|
||||
app := &App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
OverrideKubeContext: "default",
|
||||
Logger: newAppTestLogger(),
|
||||
Namespace: "",
|
||||
Env: "default",
|
||||
FileOrDir: "helmfile.yaml",
|
||||
}
|
||||
|
||||
expectNoCallsToHelm(app)
|
||||
|
||||
app = injectFs(app, fs)
|
||||
|
||||
err := app.ForEachState(
|
||||
Noop,
|
||||
false,
|
||||
SetFilter(true),
|
||||
)
|
||||
|
||||
if c.isValid && err != nil {
|
||||
t.Errorf("[case: %d] Unexpected error for valid case: %v", idx, err)
|
||||
} else if !c.isValid {
|
||||
var invalidUpdateStrategy state.InvalidUpdateStrategyError
|
||||
invalidUpdateStrategy.UpdateStrategy = c.updateStrategy
|
||||
if err == nil {
|
||||
t.Errorf("[case: %d] Expected error for invalid case", idx)
|
||||
} else if !strings.Contains(err.Error(), invalidUpdateStrategy.Error()) {
|
||||
t.Errorf("[case: %d] Unexpected error returned for invalid case\ngot: %v\nexpected underlying error: %s", idx, err, invalidUpdateStrategy.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"/path/to/base.yaml": `
|
||||
@@ -3076,6 +3167,97 @@ baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 defau
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade with reinstallIfForbidden
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-reinstallIfForbidden",
|
||||
loc: location(),
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "foo", Chart: "stable/mychart1", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^foo$", Flags: listFlags("", "default")}: ``,
|
||||
{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 default
|
||||
`,
|
||||
{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
upgraded: []exectest.Release{
|
||||
{Name: "baz", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "bar", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "foo", Flags: []string{"--kube-context", "default"}},
|
||||
},
|
||||
deleted: []exectest.Release{},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade and --skip-diff-on-install with reinstallIfForbidden
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-skip-diff-on-install-with-reinstallIfForbidden",
|
||||
loc: location(),
|
||||
skipDiffOnInstall: true,
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^foo$", Flags: listFlags("", "default")}: ``,
|
||||
{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 default
|
||||
`,
|
||||
{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
upgraded: []exectest.Release{
|
||||
{Name: "baz", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "bar", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "foo", Flags: []string{"--kube-context", "default"}},
|
||||
},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// upgrades
|
||||
//
|
||||
{
|
||||
@@ -3772,7 +3954,7 @@ releases:
|
||||
}
|
||||
for flagIdx := range wantDeletes[relIdx].Flags {
|
||||
if wantDeletes[relIdx].Flags[flagIdx] != helm.Deleted[relIdx].Flags[flagIdx] {
|
||||
t.Errorf("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
|
||||
t.Errorf("releases[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/helmfile/vals"
|
||||
@@ -285,6 +286,18 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba
|
||||
}
|
||||
}
|
||||
|
||||
// Validate updateStrategy value if set in the releases
|
||||
for i := range finalState.Releases {
|
||||
if finalState.Releases[i].UpdateStrategy != "" {
|
||||
if !slices.Contains(state.ValidUpdateStrategyValues, finalState.Releases[i].UpdateStrategy) {
|
||||
return nil, &state.StateLoadError{
|
||||
Msg: fmt.Sprintf("failed to read %s", finalState.FilePath),
|
||||
Cause: &state.InvalidUpdateStrategyError{UpdateStrategy: finalState.Releases[i].UpdateStrategy},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalState.OrginReleases = finalState.Releases
|
||||
return finalState, nil
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
1 release(s) matching name=a found in helmfile.yaml
|
||||
|
||||
processing 1 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default/default/a
|
||||
|
||||
processing releases in group 1/1: default/default/a
|
||||
changing working directory back to "/path/to"
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) UPDATED
|
||||
baz (stable/mychart3) UPDATED
|
||||
foo (stable/mychart1) UPDATED
|
||||
|
||||
invoking preapply hooks for 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//foo
|
||||
2 default//baz, default//bar
|
||||
|
||||
invoking preapply hooks for releases in group 1/2: default//foo
|
||||
invoking preapply hooks for releases in group 2/2: default//baz, default//bar
|
||||
processing 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//baz, default//bar
|
||||
2 default//foo
|
||||
|
||||
processing releases in group 1/2: default//baz, default//bar
|
||||
update strategy - sync success
|
||||
update strategy - sync success
|
||||
processing releases in group 2/2: default//foo
|
||||
getting deployed release version failed: Failed to get the version for: mychart1
|
||||
|
||||
UPDATED RELEASES:
|
||||
NAME NAMESPACE CHART VERSION DURATION
|
||||
baz stable/mychart3 3.1.0 0s
|
||||
bar stable/mychart2 3.1.0 0s
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) UPDATED
|
||||
baz (stable/mychart3) UPDATED
|
||||
foo (stable/mychart1) UPDATED
|
||||
|
||||
invoking preapply hooks for 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//foo
|
||||
2 default//baz, default//bar
|
||||
|
||||
invoking preapply hooks for releases in group 1/2: default//foo
|
||||
invoking preapply hooks for releases in group 2/2: default//baz, default//bar
|
||||
processing 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//baz, default//bar
|
||||
2 default//foo
|
||||
|
||||
processing releases in group 1/2: default//baz, default//bar
|
||||
update strategy - sync success
|
||||
update strategy - sync success
|
||||
processing releases in group 2/2: default//foo
|
||||
getting deployed release version failed: Failed to get the version for: mychart1
|
||||
|
||||
UPDATED RELEASES:
|
||||
NAME NAMESPACE CHART VERSION DURATION
|
||||
baz stable/mychart3 3.1.0 0s
|
||||
bar stable/mychart2 3.1.0 0s
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
Reference in New Issue
Block a user