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>
This commit is contained in:
parent
c41f3e88d2
commit
ec70bc5b2d
|
|
@ -329,6 +329,8 @@ releases:
|
|||
reuseValues: false
|
||||
# set `false` to uninstall this release on sync. (default true)
|
||||
installed: true
|
||||
# when set to "reinstall", an uninstall will be performed before the update
|
||||
updateStrategy: ""
|
||||
# restores previous state in case of failed release (default false)
|
||||
atomic: true
|
||||
# when true, cleans up any new resources created during a failed release (default false)
|
||||
|
|
|
|||
|
|
@ -1392,7 +1392,7 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
|
|||
SuppressOutputLineRegex: c.SuppressOutputLineRegex(),
|
||||
}
|
||||
|
||||
infoMsg, releasesToBeUpdated, releasesToBeDeleted, errs := r.diff(false, detailedExitCode, c, diffOpts)
|
||||
infoMsg, releasesToBeUpdated, releasesToBeReinstalled, releasesToBeDeleted, errs := r.diff(false, detailedExitCode, c, diffOpts)
|
||||
if len(errs) > 0 {
|
||||
return false, false, errs
|
||||
}
|
||||
|
|
@ -1407,13 +1407,19 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
|
|||
toUpdate = append(toUpdate, r)
|
||||
}
|
||||
|
||||
for _, r := range releasesToBeReinstalled {
|
||||
toDelete = append(toDelete, r)
|
||||
toUpdate = append(toUpdate, r)
|
||||
}
|
||||
|
||||
releasesWithNoChange := map[string]state.ReleaseSpec{}
|
||||
for _, r := range toApplyWithNeeds {
|
||||
release := r
|
||||
id := state.ReleaseToID(&release)
|
||||
_, uninstalled := releasesToBeDeleted[id]
|
||||
_, updated := releasesToBeUpdated[id]
|
||||
if !uninstalled && !updated {
|
||||
_, reinstalled := releasesToBeReinstalled[id]
|
||||
if !uninstalled && !updated && !reinstalled {
|
||||
releasesWithNoChange[id] = release
|
||||
}
|
||||
}
|
||||
|
|
@ -1478,7 +1484,7 @@ Do you really want to apply?
|
|||
}
|
||||
|
||||
// We upgrade releases by traversing the DAG
|
||||
if len(releasesToBeUpdated) > 0 {
|
||||
if len(releasesToBeUpdated) > 0 || len(releasesToBeReinstalled) > 0 {
|
||||
_, updateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, Reverse: false, SkipNeeds: true, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
|
||||
var rs []state.ReleaseSpec
|
||||
|
||||
|
|
@ -1487,6 +1493,9 @@ Do you really want to apply?
|
|||
if r2, ok := releasesToBeUpdated[state.ReleaseToID(&release)]; ok {
|
||||
rs = append(rs, r2)
|
||||
}
|
||||
if r2, ok := releasesToBeReinstalled[state.ReleaseToID(&release)]; ok {
|
||||
rs = append(rs, r2)
|
||||
}
|
||||
}
|
||||
|
||||
subst.Releases = rs
|
||||
|
|
@ -1525,7 +1534,7 @@ Do you really want to apply?
|
|||
a.Logger.Warnf("warn: %v\n", err)
|
||||
}
|
||||
}
|
||||
if releasesToBeDeleted == nil && releasesToBeUpdated == nil {
|
||||
if releasesToBeDeleted == nil && releasesToBeUpdated == nil && releasesToBeReinstalled == nil {
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
|
|
@ -1610,7 +1619,7 @@ Do you really want to delete?
|
|||
func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) {
|
||||
var (
|
||||
infoMsg *string
|
||||
updated, deleted map[string]state.ReleaseSpec
|
||||
updated, reinstalled, deleted map[string]state.ReleaseSpec
|
||||
)
|
||||
|
||||
ok, errs := a.withNeeds(r, c, true, func(st *state.HelmState) []error {
|
||||
|
|
@ -1642,12 +1651,12 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error)
|
|||
ctx: r.ctx,
|
||||
Ask: r.Ask,
|
||||
}
|
||||
infoMsg, updated, deleted, errs = filtered.diff(true, c.DetailedExitcode(), c, opts)
|
||||
infoMsg, updated, reinstalled, deleted, errs = filtered.diff(true, c.DetailedExitcode(), c, opts)
|
||||
|
||||
return errs
|
||||
})
|
||||
|
||||
return infoMsg, ok, len(deleted) > 0 || len(updated) > 0, errs
|
||||
return infoMsg, ok, len(deleted) > 0 || len(updated) > 0 || len(reinstalled) > 0, errs
|
||||
}
|
||||
|
||||
func (a *App) lint(r *Run, c LintConfigProvider) (bool, []error, []error) {
|
||||
|
|
|
|||
|
|
@ -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: reinstall
|
||||
- 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"}},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3073,6 +3073,106 @@ baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 defau
|
|||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade with reinstall
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-reinstall",
|
||||
loc: location(),
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstall
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstall
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --detailed-exitcode --reset-values"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "foo", Chart: "stable/mychart1", Flags: "--disable-validation --kube-context default --detailed-exitcode --reset-values"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --detailed-exitcode --reset-values"}: 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{
|
||||
// These releases have updateStrategy=reinstall which will be both uninstalled and installed
|
||||
{Name: "baz", Flags: []string{}},
|
||||
{Name: "bar", Flags: []string{}},
|
||||
},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade and --skip-diff-on-install with reinstall
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-skip-diff-on-install-with-reinstall",
|
||||
loc: location(),
|
||||
skipDiffOnInstall: true,
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstall
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstall
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --detailed-exitcode --reset-values"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --detailed-exitcode --reset-values"}: 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{
|
||||
// These releases have updateStrategy=reinstall which will be both uninstalled and installed
|
||||
{Name: "baz", Flags: []string{}},
|
||||
{Name: "bar", Flags: []string{}},
|
||||
},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// upgrades
|
||||
//
|
||||
{
|
||||
|
|
@ -3769,7 +3869,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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,12 +137,13 @@ func (r *Run) Repos(c ReposConfigProvider) error {
|
|||
return r.ctx.SyncReposOnce(r.state, r.helm)
|
||||
}
|
||||
|
||||
func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfigProvider, diffOpts *state.DiffOpts) (*string, map[string]state.ReleaseSpec, map[string]state.ReleaseSpec, []error) {
|
||||
func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfigProvider, diffOpts *state.DiffOpts) (*string, map[string]state.ReleaseSpec, map[string]state.ReleaseSpec, map[string]state.ReleaseSpec, []error) {
|
||||
st := r.state
|
||||
helm := r.helm
|
||||
|
||||
var changedReleases []state.ReleaseSpec
|
||||
var deletingReleases []state.ReleaseSpec
|
||||
var reinstallingReleases []state.ReleaseSpec
|
||||
var planningErrs []error
|
||||
|
||||
// TODO Better way to detect diff on only filtered releases
|
||||
|
|
@ -154,6 +155,10 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
|
|||
if err != nil {
|
||||
planningErrs = append(planningErrs, err)
|
||||
}
|
||||
reinstallingReleases, err = st.DetectReleasesToBeReinstalledForSync(helm, st.Releases)
|
||||
if err != nil {
|
||||
planningErrs = append(planningErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
fatalErrs := []error{}
|
||||
|
|
@ -170,7 +175,7 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
|
|||
}
|
||||
|
||||
if len(fatalErrs) > 0 {
|
||||
return nil, nil, nil, fatalErrs
|
||||
return nil, nil, nil, nil, fatalErrs
|
||||
}
|
||||
|
||||
releasesToBeDeleted := map[string]state.ReleaseSpec{}
|
||||
|
|
@ -180,6 +185,14 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
|
|||
releasesToBeDeleted[id] = release
|
||||
}
|
||||
|
||||
releasesWithReinstalled := map[string]state.ReleaseSpec{}
|
||||
for _, r := range reinstallingReleases {
|
||||
release := r
|
||||
id := state.ReleaseToID(&release)
|
||||
releasesWithReinstalled[id] = release
|
||||
}
|
||||
|
||||
releasesToBeReinstalled := map[string]state.ReleaseSpec{}
|
||||
releasesToBeUpdated := map[string]state.ReleaseSpec{}
|
||||
for _, r := range changedReleases {
|
||||
release := r
|
||||
|
|
@ -187,25 +200,36 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
|
|||
|
||||
// If `helm-diff` detected changes but it is not being `helm delete`ed, we should run `helm upgrade`
|
||||
if _, ok := releasesToBeDeleted[id]; !ok {
|
||||
// Is the release with "reinstall" update strategy
|
||||
if _, ok := releasesWithReinstalled[id]; ok {
|
||||
// Make sure we wait on the delete in the case of a reinstall
|
||||
deleteWait := true
|
||||
release.DeleteWait = &deleteWait
|
||||
releasesToBeReinstalled[id] = release
|
||||
} else {
|
||||
releasesToBeUpdated[id] = release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sync only when there are changes
|
||||
if len(releasesToBeUpdated) == 0 && len(releasesToBeDeleted) == 0 {
|
||||
if len(releasesToBeUpdated) == 0 && len(releasesToBeDeleted) == 0 && len(releasesToBeReinstalled) == 0 {
|
||||
var msg *string
|
||||
if c.DetailedExitcode() {
|
||||
// TODO better way to get the logger
|
||||
m := "No affected releases"
|
||||
msg = &m
|
||||
}
|
||||
return msg, nil, nil, nil
|
||||
return msg, nil, nil, nil, nil
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for _, r := range releasesToBeUpdated {
|
||||
names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart))
|
||||
}
|
||||
for _, r := range releasesToBeReinstalled {
|
||||
names = append(names, fmt.Sprintf(" %s (%s) REINSTALLED", r.Name, r.Chart))
|
||||
}
|
||||
for _, r := range releasesToBeDeleted {
|
||||
releaseToBeDeleted := fmt.Sprintf(" %s (%s) DELETED", r.Name, r.Chart)
|
||||
if c.Color() {
|
||||
|
|
@ -220,5 +244,5 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
|
|||
%s
|
||||
`, strings.Join(names, "\n"))
|
||||
|
||||
return &infoMsg, releasesToBeUpdated, releasesToBeDeleted, nil
|
||||
return &infoMsg, releasesToBeUpdated, releasesToBeReinstalled, releasesToBeDeleted, nil
|
||||
}
|
||||
|
|
|
|||
42
pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
vendored
Normal file
42
pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||
first-pass uses: &{default map[] map[]}
|
||||
first-pass rendering output of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: a
|
||||
3: chart: incubator/raw
|
||||
4: namespace: default
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: b
|
||||
7: chart: incubator/raw
|
||||
8: namespace: default
|
||||
9:
|
||||
|
||||
first-pass produced: &{default map[] map[]}
|
||||
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
|
||||
vals:
|
||||
map[]
|
||||
defaultVals:[]
|
||||
second-pass rendering result of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: a
|
||||
3: chart: incubator/raw
|
||||
4: namespace: default
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: b
|
||||
7: chart: incubator/raw
|
||||
8: namespace: default
|
||||
9:
|
||||
|
||||
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"
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||
first-pass uses: &{default map[] map[]}
|
||||
first-pass rendering output of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: baz
|
||||
3: chart: stable/mychart3
|
||||
4: disableValidationOnInstall: true
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: foo
|
||||
7: chart: stable/mychart1
|
||||
8: disableValidationOnInstall: true
|
||||
9: needs:
|
||||
10: - bar
|
||||
11: - name: bar
|
||||
12: chart: stable/mychart2
|
||||
13: disableValidation: true
|
||||
14: updateStrategy: reinstall
|
||||
15:
|
||||
|
||||
first-pass produced: &{default map[] map[]}
|
||||
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
|
||||
vals:
|
||||
map[]
|
||||
defaultVals:[]
|
||||
second-pass rendering result of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: baz
|
||||
3: chart: stable/mychart3
|
||||
4: disableValidationOnInstall: true
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: foo
|
||||
7: chart: stable/mychart1
|
||||
8: disableValidationOnInstall: true
|
||||
9: needs:
|
||||
10: - bar
|
||||
11: - name: bar
|
||||
12: chart: stable/mychart2
|
||||
13: disableValidation: true
|
||||
14: updateStrategy: reinstall
|
||||
15:
|
||||
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) REINSTALLED
|
||||
baz (stable/mychart3) REINSTALLED
|
||||
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
|
||||
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
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
74
pkg/app/testdata/testapply/install-with-upgrade-with-skip-diff-on-install-with-reinstall/log
vendored
Normal file
74
pkg/app/testdata/testapply/install-with-upgrade-with-skip-diff-on-install-with-reinstall/log
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||
first-pass uses: &{default map[] map[]}
|
||||
first-pass rendering output of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: baz
|
||||
3: chart: stable/mychart3
|
||||
4: disableValidationOnInstall: true
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: foo
|
||||
7: chart: stable/mychart1
|
||||
8: disableValidationOnInstall: true
|
||||
9: needs:
|
||||
10: - bar
|
||||
11: - name: bar
|
||||
12: chart: stable/mychart2
|
||||
13: disableValidation: true
|
||||
14: updateStrategy: reinstall
|
||||
15:
|
||||
|
||||
first-pass produced: &{default map[] map[]}
|
||||
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
|
||||
vals:
|
||||
map[]
|
||||
defaultVals:[]
|
||||
second-pass rendering result of "helmfile.yaml.part.0":
|
||||
0:
|
||||
1: releases:
|
||||
2: - name: baz
|
||||
3: chart: stable/mychart3
|
||||
4: disableValidationOnInstall: true
|
||||
5: updateStrategy: reinstall
|
||||
6: - name: foo
|
||||
7: chart: stable/mychart1
|
||||
8: disableValidationOnInstall: true
|
||||
9: needs:
|
||||
10: - bar
|
||||
11: - name: bar
|
||||
12: chart: stable/mychart2
|
||||
13: disableValidation: true
|
||||
14: updateStrategy: reinstall
|
||||
15:
|
||||
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) REINSTALLED
|
||||
baz (stable/mychart3) REINSTALLED
|
||||
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
|
||||
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
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
|
|
@ -273,6 +273,8 @@ type ReleaseSpec struct {
|
|||
Force *bool `yaml:"force,omitempty"`
|
||||
// Installed, when set to true, `delete --purge` the release
|
||||
Installed *bool `yaml:"installed,omitempty"`
|
||||
// UpdateStrategy, when set, indicate the strategy to use to update the release
|
||||
UpdateStrategy string `yaml:"updateStrategy,omitempty"`
|
||||
// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
|
||||
Atomic *bool `yaml:"atomic,omitempty"`
|
||||
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
||||
|
|
@ -457,6 +459,7 @@ type SetValue struct {
|
|||
// AffectedReleases hold the list of released that where updated, deleted, or in error
|
||||
type AffectedReleases struct {
|
||||
Upgraded []*ReleaseSpec
|
||||
Reinstalled []*ReleaseSpec
|
||||
Deleted []*ReleaseSpec
|
||||
Failed []*ReleaseSpec
|
||||
DeleteFailed []*ReleaseSpec
|
||||
|
|
@ -775,6 +778,26 @@ func (st *HelmState) DetectReleasesToBeDeletedForSync(helm helmexec.Interface, r
|
|||
return deleted, nil
|
||||
}
|
||||
|
||||
func (st *HelmState) DetectReleasesToBeReinstalledForSync(helm helmexec.Interface, releases []ReleaseSpec) ([]ReleaseSpec, error) {
|
||||
reinstalled := []ReleaseSpec{}
|
||||
for i := range releases {
|
||||
release := releases[i]
|
||||
|
||||
if release.Desired() && (release.UpdateStrategy == "reinstall" || release.UpdateStrategy == "reinstallIfForbidden") {
|
||||
installed, err := st.isReleaseInstalled(st.createHelmContext(&release, 0), helm, release)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if installed {
|
||||
// Otherwise `release` messed up(https://github.com/roboll/helmfile/issues/554)
|
||||
r := release
|
||||
reinstalled = append(reinstalled, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return reinstalled, nil
|
||||
}
|
||||
|
||||
func (st *HelmState) DetectReleasesToBeDeleted(helm helmexec.Interface, releases []ReleaseSpec) ([]ReleaseSpec, error) {
|
||||
detected := []ReleaseSpec{}
|
||||
for i := range releases {
|
||||
|
|
@ -1026,7 +1049,79 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
|
|||
}
|
||||
m.Unlock()
|
||||
}
|
||||
} else if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
} else if release.UpdateStrategy == "reinstall" || release.UpdateStrategy == "reinstallIfForbidden" {
|
||||
syncSuccessful := false
|
||||
if release.UpdateStrategy == "reinstallIfForbidden" {
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
st.logger.Debugf("reinstallIfForbidden update strategy - sync failed: %s", err.Error())
|
||||
// Only fail if a different error than forbidden updates
|
||||
if !strings.Contains(err.Error(), "Forbidden: updates") {
|
||||
st.logger.Debugf("reinstallIfForbidden update strategy - sync failed not due to Fobidden updates")
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
}
|
||||
} else {
|
||||
st.logger.Debugf("reinstallIfForbidden update strategy - sync success")
|
||||
syncSuccessful = true
|
||||
m.Lock()
|
||||
affectedReleases.Upgraded = append(affectedReleases.Upgraded, release)
|
||||
m.Unlock()
|
||||
installedVersion, err := st.getDeployedVersion(context, helm, release)
|
||||
if err != nil { // err is not really impacting so just log it
|
||||
st.logger.Debugf("getting deployed release version failed: %v", err)
|
||||
} else {
|
||||
release.installedVersion = installedVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't attempt to reinstall if a sync was successful
|
||||
if !syncSuccessful {
|
||||
st.logger.Debugf("reinstallIfForbidden update strategy - reinstalling...")
|
||||
installed, err := st.isReleaseInstalled(context, helm, *release)
|
||||
if err != nil {
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
}
|
||||
if installed {
|
||||
var args []string
|
||||
if release.Namespace != "" {
|
||||
args = append(args, "--namespace", release.Namespace)
|
||||
}
|
||||
args = st.appendDeleteWaitFlags(args, release)
|
||||
deletionFlags := st.appendConnectionFlags(args, release)
|
||||
m.Lock()
|
||||
if _, err := st.triggerReleaseEvent("preuninstall", nil, release, "sync"); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
} else if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
} else if _, err := st.triggerReleaseEvent("postuninstall", nil, release, "sync"); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
relErr = newReleaseFailedError(release, err)
|
||||
} else {
|
||||
m.Lock()
|
||||
affectedReleases.Reinstalled = append(affectedReleases.Reinstalled, release)
|
||||
m.Unlock()
|
||||
installedVersion, err := st.getDeployedVersion(context, helm, release)
|
||||
if err != nil { // err is not really impacting so just log it
|
||||
st.logger.Debugf("getting deployed release version failed: %v", err)
|
||||
} else {
|
||||
release.installedVersion = installedVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
|
|
@ -1042,6 +1137,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
|
|||
release.installedVersion = installedVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := st.triggerPostsyncEvent(release, relErr, "sync"); err != nil {
|
||||
if relErr == nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue