fix: Fix --selector to not break `needs` (#934)
This commit is contained in:
parent
dad8e72bc5
commit
f41fe86452
116
pkg/app/app.go
116
pkg/app/app.go
|
|
@ -98,13 +98,13 @@ func Init(app *App) *App {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Deps(c DepsConfigProvider) error {
|
func (a *App) Deps(c DepsConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Deps(c)
|
return run.Deps(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Repos(c ReposConfigProvider) error {
|
func (a *App) Repos(c ReposConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Repos(c)
|
return run.Repos(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -116,68 +116,68 @@ func (a *App) reverse() *App {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
|
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.DeprecatedSyncCharts(c)
|
return run.DeprecatedSyncCharts(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Diff(c DiffConfigProvider) error {
|
func (a *App) Diff(c DiffConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Diff(c)
|
return run.Diff(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Template(c TemplateConfigProvider) error {
|
func (a *App) Template(c TemplateConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Template(c)
|
return run.Template(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Lint(c LintConfigProvider) error {
|
func (a *App) Lint(c LintConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Lint(c)
|
return run.Lint(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Sync(c SyncConfigProvider) error {
|
func (a *App) Sync(c SyncConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Sync(c)
|
return run.Sync(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Apply(c ApplyConfigProvider) error {
|
func (a *App) Apply(c ApplyConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachState(func(run *Run) (bool, []error) {
|
||||||
return a.apply(run, c)
|
return a.apply(run, c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Status(c StatusesConfigProvider) error {
|
func (a *App) Status(c StatusesConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Status(c)
|
return run.Status(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Delete(c DeleteConfigProvider) error {
|
func (a *App) Delete(c DeleteConfigProvider) error {
|
||||||
return a.reverse().ForEachState(func(run *Run) []error {
|
return a.reverse().ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Delete(c)
|
return run.Delete(c)
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Destroy(c DestroyConfigProvider) error {
|
func (a *App) Destroy(c DestroyConfigProvider) error {
|
||||||
return a.reverse().ForEachState(func(run *Run) []error {
|
return a.reverse().ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Destroy(c)
|
return run.Destroy(c)
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Test(c TestConfigProvider) error {
|
func (a *App) Test(c TestConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
return run.Test(c)
|
return run.Test(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) PrintState(c StateConfigProvider) error {
|
func (a *App) PrintState(c StateConfigProvider) error {
|
||||||
|
|
||||||
return a.ForEachState(func(run *Run) []error {
|
return a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
state, err := run.state.ToYaml()
|
state, err := run.state.ToYaml()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
|
|
@ -191,7 +191,7 @@ func (a *App) ListReleases(c StateConfigProvider) error {
|
||||||
table := uitable.New()
|
table := uitable.New()
|
||||||
table.AddRow("NAME", "NAMESPACE", "INSTALLED", "LABELS")
|
table.AddRow("NAME", "NAMESPACE", "INSTALLED", "LABELS")
|
||||||
|
|
||||||
err := a.ForEachState(func(run *Run) []error {
|
err := a.ForEachStateFiltered(func(run *Run) []error {
|
||||||
//var releases m
|
//var releases m
|
||||||
for _, r := range run.state.Releases {
|
for _, r := range run.state.Releases {
|
||||||
labels := ""
|
labels := ""
|
||||||
|
|
@ -399,7 +399,7 @@ func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*sta
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ForEachState(do func(*Run) []error, dagEnabled ...bool) error {
|
func (a *App) ForEachStateFiltered(do func(*Run) []error, dagEnabled ...bool) error {
|
||||||
ctx := NewContext()
|
ctx := NewContext()
|
||||||
err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) []error {
|
err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) []error {
|
||||||
run := NewRun(st, helm, ctx)
|
run := NewRun(st, helm, ctx)
|
||||||
|
|
@ -414,6 +414,20 @@ func (a *App) ForEachState(do func(*Run) []error, dagEnabled ...bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) ForEachState(do func(*Run) (bool, []error)) error {
|
||||||
|
ctx := NewContext()
|
||||||
|
err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState, helm helmexec.Interface) (bool, []error) {
|
||||||
|
run := NewRun(st, helm, ctx)
|
||||||
|
return do(run)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && a.ErrorHandler != nil {
|
||||||
|
return a.ErrorHandler(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func printBatches(batches [][]state.Release) string {
|
func printBatches(batches [][]state.Release) string {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
|
@ -644,7 +658,7 @@ func (a *App) findDesiredStateFiles(specifiedPath string) ([]string, error) {
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) apply(r *Run, c ApplyConfigProvider) []error {
|
func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, []error) {
|
||||||
st := r.state
|
st := r.state
|
||||||
helm := r.helm
|
helm := r.helm
|
||||||
ctx := r.ctx
|
ctx := r.ctx
|
||||||
|
|
@ -652,14 +666,14 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) []error {
|
||||||
affectedReleases := state.AffectedReleases{}
|
affectedReleases := state.AffectedReleases{}
|
||||||
if !c.SkipDeps() {
|
if !c.SkipDeps() {
|
||||||
if errs := ctx.SyncReposOnce(st, helm); errs != nil && len(errs) > 0 {
|
if errs := ctx.SyncReposOnce(st, helm); errs != nil && len(errs) > 0 {
|
||||||
return errs
|
return false, errs
|
||||||
}
|
}
|
||||||
if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 {
|
if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 {
|
||||||
return errs
|
return false, errs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if errs := st.PrepareReleases(helm, "apply"); errs != nil && len(errs) > 0 {
|
if errs := st.PrepareReleases(helm, "apply"); errs != nil && len(errs) > 0 {
|
||||||
return errs
|
return false, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
// helm must be 2.11+ and helm-diff should be provided `--detailed-exitcode` in order for `helmfile apply` to work properly
|
// helm must be 2.11+ and helm-diff should be provided `--detailed-exitcode` in order for `helmfile apply` to work properly
|
||||||
|
|
@ -671,29 +685,53 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) []error {
|
||||||
Set: c.Set(),
|
Set: c.Set(),
|
||||||
}
|
}
|
||||||
|
|
||||||
changedReleases, errs := st.DiffReleases(helm, c.Values(), c.Concurrency(), detailedExitCode, c.SuppressSecrets(), false, diffOpts)
|
allReleases := st.Releases
|
||||||
|
|
||||||
deletingReleases, err := st.DetectReleasesToBeDeleted(helm, st.Releases)
|
var changedReleases []state.ReleaseSpec
|
||||||
|
var deletingReleases []state.ReleaseSpec
|
||||||
|
var planningErrs []error
|
||||||
|
|
||||||
|
// TODO Better way to detect diff on only filtered releases
|
||||||
|
{
|
||||||
|
if len(st.Selectors) > 0 {
|
||||||
|
releases, err := st.GetFilteredReleases()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
return false, []error{err}
|
||||||
}
|
}
|
||||||
|
if len(releases) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
st.Releases = releases
|
||||||
|
}
|
||||||
|
|
||||||
|
changedReleases, planningErrs = st.DiffReleases(helm, c.Values(), c.Concurrency(), detailedExitCode, c.SuppressSecrets(), false, diffOpts)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
deletingReleases, err = st.DetectReleasesToBeDeleted(helm, st.Releases)
|
||||||
|
if err != nil {
|
||||||
|
planningErrs = append(planningErrs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
st.Releases = allReleases
|
||||||
|
|
||||||
fatalErrs := []error{}
|
fatalErrs := []error{}
|
||||||
|
|
||||||
noError := true
|
for _, e := range planningErrs {
|
||||||
for _, e := range errs {
|
|
||||||
switch err := e.(type) {
|
switch err := e.(type) {
|
||||||
case *state.ReleaseError:
|
case *state.ReleaseError:
|
||||||
if err.Code != 2 {
|
if err.Code != 2 {
|
||||||
noError = false
|
|
||||||
fatalErrs = append(fatalErrs, e)
|
fatalErrs = append(fatalErrs, e)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
noError = false
|
|
||||||
fatalErrs = append(fatalErrs, e)
|
fatalErrs = append(fatalErrs, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(fatalErrs) > 0 {
|
||||||
|
return false, fatalErrs
|
||||||
|
}
|
||||||
|
|
||||||
releasesToBeDeleted := map[string]state.ReleaseSpec{}
|
releasesToBeDeleted := map[string]state.ReleaseSpec{}
|
||||||
for _, r := range deletingReleases {
|
for _, r := range deletingReleases {
|
||||||
id := state.ReleaseToID(&r)
|
id := state.ReleaseToID(&r)
|
||||||
|
|
@ -711,13 +749,14 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) []error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync only when there are changes
|
// sync only when there are changes
|
||||||
if noError {
|
|
||||||
if len(releasesToBeUpdated) == 0 && len(releasesToBeDeleted) == 0 {
|
if len(releasesToBeUpdated) == 0 && len(releasesToBeDeleted) == 0 {
|
||||||
// TODO better way to get the logger
|
// TODO better way to get the logger
|
||||||
logger := c.Logger()
|
logger := c.Logger()
|
||||||
logger.Infof("")
|
logger.Infof("")
|
||||||
logger.Infof("No affected releases")
|
logger.Infof("No affected releases")
|
||||||
} else {
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
names := []string{}
|
names := []string{}
|
||||||
for _, r := range releasesToBeUpdated {
|
for _, r := range releasesToBeUpdated {
|
||||||
names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart))
|
names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart))
|
||||||
|
|
@ -740,12 +779,15 @@ Do you really want to apply?
|
||||||
if !interactive {
|
if !interactive {
|
||||||
a.Logger.Debug(infoMsg)
|
a.Logger.Debug(infoMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncErrs := []error{}
|
||||||
|
|
||||||
if !interactive || interactive && r.askForConfirmation(confMsg) {
|
if !interactive || interactive && r.askForConfirmation(confMsg) {
|
||||||
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
|
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
|
||||||
|
|
||||||
// We deleted releases by traversing the DAG in reverse order
|
// We deleted releases by traversing the DAG in reverse order
|
||||||
if len(releasesToBeDeleted) > 0 {
|
if len(releasesToBeDeleted) > 0 {
|
||||||
_, errs := withDAG(st, helm, a.Logger, true, a.Wrap(func(subst *state.HelmState, helm helmexec.Interface) []error {
|
_, deletionErrs := withDAG(st, helm, a.Logger, true, a.Wrap(func(subst *state.HelmState, helm helmexec.Interface) []error {
|
||||||
var rs []state.ReleaseSpec
|
var rs []state.ReleaseSpec
|
||||||
|
|
||||||
for _, r := range subst.Releases {
|
for _, r := range subst.Releases {
|
||||||
|
|
@ -759,14 +801,14 @@ Do you really want to apply?
|
||||||
return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency())
|
return subst.DeleteReleasesForSync(&affectedReleases, helm, c.Concurrency())
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if errs != nil && len(errs) > 0 {
|
if deletionErrs != nil && len(deletionErrs) > 0 {
|
||||||
return errs
|
syncErrs = append(syncErrs, deletionErrs...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We upgrade releases by traversing the DAG
|
// We upgrade releases by traversing the DAG
|
||||||
if len(releasesToBeUpdated) > 0 {
|
if len(releasesToBeUpdated) > 0 {
|
||||||
_, errs := withDAG(st, helm, a.Logger, false, a.Wrap(func(subst *state.HelmState, helm helmexec.Interface) []error {
|
_, updateErrs := withDAG(st, helm, a.Logger, false, a.Wrap(func(subst *state.HelmState, helm helmexec.Interface) []error {
|
||||||
var rs []state.ReleaseSpec
|
var rs []state.ReleaseSpec
|
||||||
|
|
||||||
for _, r := range subst.Releases {
|
for _, r := range subst.Releases {
|
||||||
|
|
@ -783,18 +825,14 @@ Do you really want to apply?
|
||||||
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), &syncOpts)
|
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), &syncOpts)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if errs != nil && len(errs) > 0 {
|
if updateErrs != nil && len(updateErrs) > 0 {
|
||||||
return errs
|
syncErrs = append(syncErrs, updateErrs...)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
affectedReleases.DisplayAffectedReleases(c.Logger())
|
affectedReleases.DisplayAffectedReleases(c.Logger())
|
||||||
return fatalErrs
|
return true, syncErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileExistsAt(path string) bool {
|
func fileExistsAt(path string) bool {
|
||||||
|
|
|
||||||
|
|
@ -2061,6 +2061,7 @@ func TestApply(t *testing.T) {
|
||||||
concurrency int
|
concurrency int
|
||||||
error string
|
error string
|
||||||
files map[string]string
|
files map[string]string
|
||||||
|
selectors []string
|
||||||
lists map[exectest.ListKey]string
|
lists map[exectest.ListKey]string
|
||||||
diffs map[exectest.DiffKey]error
|
diffs map[exectest.DiffKey]error
|
||||||
upgraded []exectest.Release
|
upgraded []exectest.Release
|
||||||
|
|
@ -2361,6 +2362,22 @@ worker 1/1 finished
|
||||||
worker 1/1 started
|
worker 1/1 started
|
||||||
getting deployed release version failed:unexpected list key: {^frontend-v3$ --kube-contextdefault}
|
getting deployed release version failed:unexpected list key: {^frontend-v3$ --kube-contextdefault}
|
||||||
worker 1/1 finished
|
worker 1/1 finished
|
||||||
|
|
||||||
|
UPDATED RELEASES:
|
||||||
|
NAME CHART VERSION
|
||||||
|
front-proxy stable/envoy
|
||||||
|
logging charts/fluent-bit
|
||||||
|
database charts/mysql
|
||||||
|
servicemesh charts/istio
|
||||||
|
anotherbackend charts/anotherbackend
|
||||||
|
backend-v2 charts/backend
|
||||||
|
frontend-v3 charts/frontend
|
||||||
|
|
||||||
|
|
||||||
|
DELETED RELEASES:
|
||||||
|
NAME
|
||||||
|
frontend-v1
|
||||||
|
backend-v1
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
|
|
@ -2487,6 +2504,13 @@ worker 1/1 finished
|
||||||
worker 1/1 started
|
worker 1/1 started
|
||||||
getting deployed release version failed:unexpected list key: {^foo$ --kube-contextdefault}
|
getting deployed release version failed:unexpected list key: {^foo$ --kube-contextdefault}
|
||||||
worker 1/1 finished
|
worker 1/1 finished
|
||||||
|
|
||||||
|
UPDATED RELEASES:
|
||||||
|
NAME CHART VERSION
|
||||||
|
bar mychart2
|
||||||
|
baz mychart3
|
||||||
|
foo mychart1
|
||||||
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
|
|
@ -2756,6 +2780,12 @@ worker 1/1 finished
|
||||||
worker 1/1 started
|
worker 1/1 started
|
||||||
getting deployed release version failed:unexpected list key: {^bar$ --tiller-namespacetns2--kube-contextdefault}
|
getting deployed release version failed:unexpected list key: {^bar$ --tiller-namespacetns2--kube-contextdefault}
|
||||||
worker 1/1 finished
|
worker 1/1 finished
|
||||||
|
|
||||||
|
UPDATED RELEASES:
|
||||||
|
NAME CHART VERSION
|
||||||
|
foo mychart1
|
||||||
|
bar mychart2
|
||||||
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
|
|
@ -2967,6 +2997,251 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
//
|
//
|
||||||
|
// upgrades with selector
|
||||||
|
//
|
||||||
|
{
|
||||||
|
// see https://github.com/roboll/helmfile/issues/919#issuecomment-549831747
|
||||||
|
name: "upgrades with good selector",
|
||||||
|
loc: location(),
|
||||||
|
files: map[string]string{
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
{{ $mark := "a" }}
|
||||||
|
|
||||||
|
releases:
|
||||||
|
- name: kubernetes-external-secrets
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: kube-system
|
||||||
|
|
||||||
|
- name: external-secrets
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: test
|
||||||
|
needs:
|
||||||
|
- kube-system/kubernetes-external-secrets
|
||||||
|
|
||||||
|
- name: my-release
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: test
|
||||||
|
needs:
|
||||||
|
- default/external-secrets
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
selectors: []string{"app=test"},
|
||||||
|
diffs: map[exectest.DiffKey]error{
|
||||||
|
exectest.DiffKey{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
exectest.DiffKey{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
},
|
||||||
|
upgraded: []exectest.Release{
|
||||||
|
{Name: "external-secrets", Flags: []string{"--kube-context", "default", "--namespace", "default"}},
|
||||||
|
{Name: "my-release", Flags: []string{"--kube-context", "default", "--namespace", "default"}},
|
||||||
|
},
|
||||||
|
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
|
||||||
|
concurrency: 1,
|
||||||
|
log: `processing file "helmfile.yaml" in directory "."
|
||||||
|
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:
|
||||||
|
2:
|
||||||
|
3: releases:
|
||||||
|
4: - name: kubernetes-external-secrets
|
||||||
|
5: chart: incubator/raw
|
||||||
|
6: namespace: kube-system
|
||||||
|
7:
|
||||||
|
8: - name: external-secrets
|
||||||
|
9: chart: incubator/raw
|
||||||
|
10: namespace: default
|
||||||
|
11: labels:
|
||||||
|
12: app: test
|
||||||
|
13: needs:
|
||||||
|
14: - kube-system/kubernetes-external-secrets
|
||||||
|
15:
|
||||||
|
16: - name: my-release
|
||||||
|
17: chart: incubator/raw
|
||||||
|
18: namespace: default
|
||||||
|
19: labels:
|
||||||
|
20: app: test
|
||||||
|
21: needs:
|
||||||
|
22: - default/external-secrets
|
||||||
|
23:
|
||||||
|
|
||||||
|
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:
|
||||||
|
2:
|
||||||
|
3: releases:
|
||||||
|
4: - name: kubernetes-external-secrets
|
||||||
|
5: chart: incubator/raw
|
||||||
|
6: namespace: kube-system
|
||||||
|
7:
|
||||||
|
8: - name: external-secrets
|
||||||
|
9: chart: incubator/raw
|
||||||
|
10: namespace: default
|
||||||
|
11: labels:
|
||||||
|
12: app: test
|
||||||
|
13: needs:
|
||||||
|
14: - kube-system/kubernetes-external-secrets
|
||||||
|
15:
|
||||||
|
16: - name: my-release
|
||||||
|
17: chart: incubator/raw
|
||||||
|
18: namespace: default
|
||||||
|
19: labels:
|
||||||
|
20: app: test
|
||||||
|
21: needs:
|
||||||
|
22: - default/external-secrets
|
||||||
|
23:
|
||||||
|
|
||||||
|
merged environment: &{default map[] map[]}
|
||||||
|
worker 1/1 started
|
||||||
|
worker 1/1 finished
|
||||||
|
worker 1/1 started
|
||||||
|
worker 1/1 finished
|
||||||
|
Affected releases are:
|
||||||
|
external-secrets (incubator/raw) UPDATED
|
||||||
|
my-release (incubator/raw) UPDATED
|
||||||
|
|
||||||
|
processing 3 groups of releases in this order:
|
||||||
|
GROUP RELEASES
|
||||||
|
1 kube-system/kubernetes-external-secrets
|
||||||
|
2 default/external-secrets
|
||||||
|
3 default/my-release
|
||||||
|
|
||||||
|
processing releases in group 1/3: kube-system/kubernetes-external-secrets
|
||||||
|
0 release(s) matching app=test found in helmfile.yaml
|
||||||
|
|
||||||
|
processing releases in group 2/3: default/external-secrets
|
||||||
|
1 release(s) matching app=test found in helmfile.yaml
|
||||||
|
|
||||||
|
worker 1/1 started
|
||||||
|
worker 1/1 finished
|
||||||
|
worker 1/1 started
|
||||||
|
getting deployed release version failed:unexpected list key: {^external-secrets$ --kube-contextdefault}
|
||||||
|
worker 1/1 finished
|
||||||
|
processing releases in group 3/3: default/my-release
|
||||||
|
1 release(s) matching app=test found in helmfile.yaml
|
||||||
|
|
||||||
|
worker 1/1 started
|
||||||
|
worker 1/1 finished
|
||||||
|
worker 1/1 started
|
||||||
|
getting deployed release version failed:unexpected list key: {^my-release$ --kube-contextdefault}
|
||||||
|
worker 1/1 finished
|
||||||
|
|
||||||
|
UPDATED RELEASES:
|
||||||
|
NAME CHART VERSION
|
||||||
|
external-secrets incubator/raw
|
||||||
|
my-release incubator/raw
|
||||||
|
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// see https://github.com/roboll/helmfile/issues/919#issuecomment-549831747
|
||||||
|
name: "upgrades with bad selector",
|
||||||
|
loc: location(),
|
||||||
|
files: map[string]string{
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
{{ $mark := "a" }}
|
||||||
|
|
||||||
|
releases:
|
||||||
|
- name: kubernetes-external-secrets
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: kube-system
|
||||||
|
|
||||||
|
- name: external-secrets
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: test
|
||||||
|
needs:
|
||||||
|
- kube-system/kubernetes-external-secrets
|
||||||
|
|
||||||
|
- name: my-release
|
||||||
|
chart: incubator/raw
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: test
|
||||||
|
needs:
|
||||||
|
- default/external-secrets
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
selectors: []string{"app=test_non_existent"},
|
||||||
|
diffs: map[exectest.DiffKey]error{},
|
||||||
|
upgraded: []exectest.Release{},
|
||||||
|
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
|
||||||
|
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
|
||||||
|
concurrency: 1,
|
||||||
|
log: `processing file "helmfile.yaml" in directory "."
|
||||||
|
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:
|
||||||
|
2:
|
||||||
|
3: releases:
|
||||||
|
4: - name: kubernetes-external-secrets
|
||||||
|
5: chart: incubator/raw
|
||||||
|
6: namespace: kube-system
|
||||||
|
7:
|
||||||
|
8: - name: external-secrets
|
||||||
|
9: chart: incubator/raw
|
||||||
|
10: namespace: default
|
||||||
|
11: labels:
|
||||||
|
12: app: test
|
||||||
|
13: needs:
|
||||||
|
14: - kube-system/kubernetes-external-secrets
|
||||||
|
15:
|
||||||
|
16: - name: my-release
|
||||||
|
17: chart: incubator/raw
|
||||||
|
18: namespace: default
|
||||||
|
19: labels:
|
||||||
|
20: app: test
|
||||||
|
21: needs:
|
||||||
|
22: - default/external-secrets
|
||||||
|
23:
|
||||||
|
|
||||||
|
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:
|
||||||
|
2:
|
||||||
|
3: releases:
|
||||||
|
4: - name: kubernetes-external-secrets
|
||||||
|
5: chart: incubator/raw
|
||||||
|
6: namespace: kube-system
|
||||||
|
7:
|
||||||
|
8: - name: external-secrets
|
||||||
|
9: chart: incubator/raw
|
||||||
|
10: namespace: default
|
||||||
|
11: labels:
|
||||||
|
12: app: test
|
||||||
|
13: needs:
|
||||||
|
14: - kube-system/kubernetes-external-secrets
|
||||||
|
15:
|
||||||
|
16: - name: my-release
|
||||||
|
17: chart: incubator/raw
|
||||||
|
18: namespace: default
|
||||||
|
19: labels:
|
||||||
|
20: app: test
|
||||||
|
21: needs:
|
||||||
|
22: - default/external-secrets
|
||||||
|
23:
|
||||||
|
|
||||||
|
merged environment: &{default map[] map[]}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
//
|
||||||
// error cases
|
// error cases
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
|
|
@ -3101,6 +3376,10 @@ err: "foo" has dependency to inexistent release "bar"
|
||||||
app.Namespace = tc.ns
|
app.Namespace = tc.ns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.selectors != nil {
|
||||||
|
app.Selectors = tc.selectors
|
||||||
|
}
|
||||||
|
|
||||||
applyErr := app.Apply(applyConfig{
|
applyErr := app.Apply(applyConfig{
|
||||||
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
|
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
|
||||||
concurrency: tc.concurrency,
|
concurrency: tc.concurrency,
|
||||||
|
|
|
||||||
|
|
@ -1258,11 +1258,10 @@ func MarkFilteredReleases(releases []ReleaseSpec, selectors []string) ([]Release
|
||||||
return filteredReleases, nil
|
return filteredReleases, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterReleases allows for the execution of helm commands against a subset of the releases in the helmfile.
|
func (st *HelmState) GetFilteredReleases() ([]ReleaseSpec, error) {
|
||||||
func (st *HelmState) FilterReleases() error {
|
|
||||||
filteredReleases, err := MarkFilteredReleases(st.Releases, st.Selectors)
|
filteredReleases, err := MarkFilteredReleases(st.Releases, st.Selectors)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
var releases []ReleaseSpec
|
var releases []ReleaseSpec
|
||||||
for _, r := range filteredReleases {
|
for _, r := range filteredReleases {
|
||||||
|
|
@ -1270,8 +1269,17 @@ func (st *HelmState) FilterReleases() error {
|
||||||
releases = append(releases, r.ReleaseSpec)
|
releases = append(releases, r.ReleaseSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return releases, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterReleases allows for the execution of helm commands against a subset of the releases in the helmfile.
|
||||||
|
func (st *HelmState) FilterReleases() error {
|
||||||
|
releases, err := st.GetFilteredReleases()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
st.Releases = releases
|
st.Releases = releases
|
||||||
st.logger.Debugf("%d release(s) matching %s found in %s\n", len(filteredReleases), strings.Join(st.Selectors, ","), st.FilePath)
|
st.logger.Debugf("%d release(s) matching %s found in %s\n", len(releases), strings.Join(st.Selectors, ","), st.FilePath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1852,9 +1860,9 @@ func renderValsSecrets(e vals.Evaluator, input ...string) ([]string, error) {
|
||||||
|
|
||||||
// DisplayAffectedReleases logs the upgraded, deleted and in error releases
|
// DisplayAffectedReleases logs the upgraded, deleted and in error releases
|
||||||
func (ar *AffectedReleases) DisplayAffectedReleases(logger *zap.SugaredLogger) {
|
func (ar *AffectedReleases) DisplayAffectedReleases(logger *zap.SugaredLogger) {
|
||||||
if ar.Upgraded != nil {
|
if ar.Upgraded != nil && len(ar.Upgraded) > 0 {
|
||||||
logger.Info("\nList of updated releases :")
|
logger.Info("\nUPDATED RELEASES:")
|
||||||
tbl, _ := prettytable.NewTable(prettytable.Column{Header: "RELEASE"},
|
tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"},
|
||||||
prettytable.Column{Header: "CHART", MinWidth: 6},
|
prettytable.Column{Header: "CHART", MinWidth: 6},
|
||||||
prettytable.Column{Header: "VERSION", AlignRight: true},
|
prettytable.Column{Header: "VERSION", AlignRight: true},
|
||||||
)
|
)
|
||||||
|
|
@ -1862,18 +1870,18 @@ func (ar *AffectedReleases) DisplayAffectedReleases(logger *zap.SugaredLogger) {
|
||||||
for _, release := range ar.Upgraded {
|
for _, release := range ar.Upgraded {
|
||||||
tbl.AddRow(release.Name, release.Chart, release.installedVersion)
|
tbl.AddRow(release.Name, release.Chart, release.installedVersion)
|
||||||
}
|
}
|
||||||
tbl.Print()
|
logger.Info(tbl.String())
|
||||||
}
|
}
|
||||||
if ar.Deleted != nil {
|
if ar.Deleted != nil && len(ar.Deleted) > 0 {
|
||||||
logger.Info("\nList of deleted releases :")
|
logger.Info("\nDELETED RELEASES:")
|
||||||
logger.Info("RELEASE")
|
logger.Info("NAME")
|
||||||
for _, release := range ar.Deleted {
|
for _, release := range ar.Deleted {
|
||||||
logger.Info(release.Name)
|
logger.Info(release.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ar.Failed != nil {
|
if ar.Failed != nil && len(ar.Failed) > 0 {
|
||||||
logger.Info("\nList of releases in error :")
|
logger.Info("\nFAILED RELEASES:")
|
||||||
logger.Info("RELEASE")
|
logger.Info("NAME")
|
||||||
for _, release := range ar.Failed {
|
for _, release := range ar.Failed {
|
||||||
logger.Info(release.Name)
|
logger.Info(release.Name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue