Do fail on a possible typo in `needs` entries (#2026)
* Do fail on a possible typo in `needs` entries Helmfile kindly fails with a friendly error when you made a typo in a `needs` entry, i.e. a `needs` entry included a reference to a release that is not defined in the helmfile config. Example Output: ``` in ./helmfile.needs.yaml: release(s) "app" depend(s) on an undefined release "infrastructure/cert-manager2". Perhaps you made a typo in `needs` or forgot defining a release named "cert-manager2" with appropriate `namespace` and `kubeContext`? ``` This prevents issues like #1959 * Fix regression in helmfile-diff (This may break when you had two or more duplicated releases that are intended to be de-duplicated before DAG calculation using selectors * Fix regression when you used selector to deduplicate releases before DAG calculation * Comments * Fix regressions in helmfile-apply and helmfile-sync * Fix regression in duplicate release detection
This commit is contained in:
parent
3d7b4287b3
commit
9efb7afb47
2
go.mod
2
go.mod
|
|
@ -29,7 +29,7 @@ require (
|
||||||
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
|
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
|
||||||
github.com/urfave/cli v1.22.5
|
github.com/urfave/cli v1.22.5
|
||||||
github.com/variantdev/chartify v0.9.1
|
github.com/variantdev/chartify v0.9.1
|
||||||
github.com/variantdev/dag v1.0.0
|
github.com/variantdev/dag v1.1.0
|
||||||
github.com/variantdev/vals v0.14.0
|
github.com/variantdev/vals v0.14.0
|
||||||
go.uber.org/multierr v1.6.0
|
go.uber.org/multierr v1.6.0
|
||||||
go.uber.org/zap v1.16.0
|
go.uber.org/zap v1.16.0
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -602,6 +602,8 @@ github.com/variantdev/chartify v0.9.1 h1:Mkq2BvtHr42BYf2a14YWkfeSoY/+d1wL2xfap4q
|
||||||
github.com/variantdev/chartify v0.9.1/go.mod h1:qF4XzQlkfH/6k2jAi1hLas+lK4zSCa8kY+r5JdmLA68=
|
github.com/variantdev/chartify v0.9.1/go.mod h1:qF4XzQlkfH/6k2jAi1hLas+lK4zSCa8kY+r5JdmLA68=
|
||||||
github.com/variantdev/dag v1.0.0 h1:7SFjATxHtrYV20P3tx53yNDBMegz6RT4jv8JPHqAHdM=
|
github.com/variantdev/dag v1.0.0 h1:7SFjATxHtrYV20P3tx53yNDBMegz6RT4jv8JPHqAHdM=
|
||||||
github.com/variantdev/dag v1.0.0/go.mod h1:pH1TQsNSLj2uxMo9NNl9zdGy01Wtn+/2MT96BrKmVyE=
|
github.com/variantdev/dag v1.0.0/go.mod h1:pH1TQsNSLj2uxMo9NNl9zdGy01Wtn+/2MT96BrKmVyE=
|
||||||
|
github.com/variantdev/dag v1.1.0 h1:xodYlSng33KWGvIGMpKUyLcIZRXKiNUx612mZJqYrDg=
|
||||||
|
github.com/variantdev/dag v1.1.0/go.mod h1:pH1TQsNSLj2uxMo9NNl9zdGy01Wtn+/2MT96BrKmVyE=
|
||||||
github.com/variantdev/vals v0.14.0 h1:J+zX1DDLTQ35A198HChVkCwLfA/4OeKhSuZ5zjmgV0Q=
|
github.com/variantdev/vals v0.14.0 h1:J+zX1DDLTQ35A198HChVkCwLfA/4OeKhSuZ5zjmgV0Q=
|
||||||
github.com/variantdev/vals v0.14.0/go.mod h1:zoPna6p+fM7pC2Lo17dl5sRpamAV2a0j2VDslkMLW0M=
|
github.com/variantdev/vals v0.14.0/go.mod h1:zoPna6p+fM7pC2Lo17dl5sRpamAV2a0j2VDslkMLW0M=
|
||||||
github.com/vektra/mockery v1.1.2/go.mod h1:VcfZjKaFOPO+MpN4ZvwPjs4c48lkq1o3Ym8yHZJu0jU=
|
github.com/vektra/mockery v1.1.2/go.mod h1:VcfZjKaFOPO+MpN4ZvwPjs4c48lkq1o3Ym8yHZJu0jU=
|
||||||
|
|
|
||||||
108
pkg/app/app.go
108
pkg/app/app.go
|
|
@ -1149,29 +1149,61 @@ func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state.
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedIds := map[string]struct{}{}
|
selectedIds := map[string]state.ReleaseSpec{}
|
||||||
|
selectedCounts := map[string]int{}
|
||||||
for _, r := range selected {
|
for _, r := range selected {
|
||||||
selectedIds[state.ReleaseToID(&r)] = struct{}{}
|
r := r
|
||||||
|
id := state.ReleaseToID(&r)
|
||||||
|
selectedIds[id] = r
|
||||||
|
selectedCounts[id]++
|
||||||
|
|
||||||
|
if dupCount := selectedCounts[id]; dupCount > 1 {
|
||||||
|
return nil, nil, fmt.Errorf("found %d duplicate releases with ID %q", dupCount, id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allReleases := r.state.GetReleasesWithOverrides()
|
allReleases := r.state.GetReleasesWithOverrides()
|
||||||
|
|
||||||
needed := map[string]struct{}{}
|
groupsByID := map[string][]*state.ReleaseSpec{}
|
||||||
for _, r := range selected {
|
|
||||||
collectNeeds(r, selectedIds, needed, allReleases, includeTransitiveNeeds)
|
|
||||||
}
|
|
||||||
|
|
||||||
var releases []state.ReleaseSpec
|
|
||||||
|
|
||||||
releases = append(releases, selected...)
|
|
||||||
|
|
||||||
for _, r := range allReleases {
|
for _, r := range allReleases {
|
||||||
if _, ok := needed[state.ReleaseToID(&r)]; ok {
|
r := r
|
||||||
releases = append(releases, r)
|
groupsByID[state.ReleaseToID(&r)] = append(groupsByID[state.ReleaseToID(&r)], &r)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkDuplicates(r.helm, r.state, releases); err != nil {
|
var deduplicated []state.ReleaseSpec
|
||||||
|
|
||||||
|
dedupedBefore := map[string]struct{}{}
|
||||||
|
|
||||||
|
// We iterate over allReleases rather than groupsByID
|
||||||
|
// to preserve the order of releases
|
||||||
|
for _, seq := range allReleases {
|
||||||
|
id := state.ReleaseToID(&seq)
|
||||||
|
|
||||||
|
rs := groupsByID[id]
|
||||||
|
|
||||||
|
if len(rs) == 1 {
|
||||||
|
deduplicated = append(deduplicated, *rs[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := dedupedBefore[id]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We keep the selected one only when there were two or more duplicate
|
||||||
|
// releases in the helmfile config.
|
||||||
|
// Otherwise we can't compute the DAG of releases correctly.
|
||||||
|
r, deduped := selectedIds[id]
|
||||||
|
if !deduped {
|
||||||
|
panic(fmt.Errorf("assertion error: release %q has never been selected. This shouldn't happen!", id))
|
||||||
|
}
|
||||||
|
|
||||||
|
deduplicated = append(deduplicated, r)
|
||||||
|
|
||||||
|
dedupedBefore[id] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkDuplicates(r.helm, r.state, deduplicated); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1183,42 +1215,7 @@ func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state.
|
||||||
|
|
||||||
a.Logger.Debugf("%d release(s)%s found in %s\n", len(selected), extra, r.state.FilePath)
|
a.Logger.Debugf("%d release(s)%s found in %s\n", len(selected), extra, r.state.FilePath)
|
||||||
|
|
||||||
return selected, releases, nil
|
return selected, deduplicated, nil
|
||||||
}
|
|
||||||
|
|
||||||
func collectNeeds(release state.ReleaseSpec, selectedIds map[string]struct{}, needed map[string]struct{}, allReleases []state.ReleaseSpec, includeTransitiveNeeds bool) {
|
|
||||||
for _, id := range release.Needs {
|
|
||||||
// Avoids duplicating a release that is selected AND also needed by another selected release
|
|
||||||
if _, ok := selectedIds[id]; !ok {
|
|
||||||
if _, ok := needed[id]; !ok {
|
|
||||||
needed[id] = struct{}{}
|
|
||||||
if includeTransitiveNeeds {
|
|
||||||
releaseParts := strings.Split(id, "/")
|
|
||||||
releasePartsCount := len(releaseParts)
|
|
||||||
releaseName := releaseParts[releasePartsCount-1]
|
|
||||||
releaseNamespace := ""
|
|
||||||
releaseKubeContext := ""
|
|
||||||
if releasePartsCount > 1 {
|
|
||||||
releaseNamespace = releaseParts[releasePartsCount-2]
|
|
||||||
}
|
|
||||||
if releasePartsCount > 2 {
|
|
||||||
releaseKubeContext = releaseParts[releasePartsCount-3]
|
|
||||||
}
|
|
||||||
for _, r := range allReleases {
|
|
||||||
if len(releaseNamespace) > 0 && r.Namespace != releaseNamespace {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(releaseKubeContext) > 0 && r.KubeContext != releaseKubeContext {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.Name == releaseName {
|
|
||||||
collectNeeds(r, selectedIds, needed, allReleases, includeTransitiveNeeds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
|
func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
|
||||||
|
|
@ -1323,6 +1320,9 @@ Do you really want to apply?
|
||||||
|
|
||||||
affectedReleases := state.AffectedReleases{}
|
affectedReleases := state.AffectedReleases{}
|
||||||
|
|
||||||
|
// Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies
|
||||||
|
st.Releases = selectedAndNeededReleases
|
||||||
|
|
||||||
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)...)
|
||||||
|
|
||||||
|
|
@ -1458,7 +1458,7 @@ Do you really want to delete?
|
||||||
func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) {
|
func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) {
|
||||||
st := r.state
|
st := r.state
|
||||||
|
|
||||||
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, false)
|
selectedReleases, deduplicatedReleases, err := a.getSelectedReleases(r, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, false, []error{err}
|
return nil, false, false, []error{err}
|
||||||
}
|
}
|
||||||
|
|
@ -1477,7 +1477,7 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error)
|
||||||
SkipDiffOnInstall: c.SkipDiffOnInstall(),
|
SkipDiffOnInstall: c.SkipDiffOnInstall(),
|
||||||
}
|
}
|
||||||
|
|
||||||
st.Releases = selectedAndNeededReleases
|
st.Releases = deduplicatedReleases
|
||||||
|
|
||||||
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: false})
|
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1731,7 +1731,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) {
|
||||||
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
|
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
|
||||||
|
|
||||||
// Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies
|
// Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies
|
||||||
st.Releases = toSyncWithNeeds
|
st.Releases = selectedAndNeededReleases
|
||||||
|
|
||||||
affectedReleases := state.AffectedReleases{}
|
affectedReleases := state.AffectedReleases{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4213,6 +4213,69 @@ merged environment: &{default map[] map[]}
|
||||||
//
|
//
|
||||||
// error cases
|
// error cases
|
||||||
//
|
//
|
||||||
|
{
|
||||||
|
name: "unselected release in needs",
|
||||||
|
loc: location(),
|
||||||
|
selectors: []string{"name=foo"},
|
||||||
|
files: map[string]string{
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
releases:
|
||||||
|
- name: bar
|
||||||
|
namespace: ns1
|
||||||
|
chart: mychart3
|
||||||
|
- name: foo
|
||||||
|
chart: mychart1
|
||||||
|
needs:
|
||||||
|
- ns1/bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
diffs: map[exectest.DiffKey]error{
|
||||||
|
exectest.DiffKey{Name: "baz", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
},
|
||||||
|
lists: map[exectest.ListKey]string{},
|
||||||
|
upgraded: []exectest.Release{},
|
||||||
|
deleted: []exectest.Release{},
|
||||||
|
concurrency: 1,
|
||||||
|
error: `in ./helmfile.yaml: release "default//foo" depends on "default/ns1/bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
|
||||||
|
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: releases:
|
||||||
|
2: - name: bar
|
||||||
|
3: namespace: ns1
|
||||||
|
4: chart: mychart3
|
||||||
|
5: - name: foo
|
||||||
|
6: chart: mychart1
|
||||||
|
7: needs:
|
||||||
|
8: - ns1/bar
|
||||||
|
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: bar
|
||||||
|
3: namespace: ns1
|
||||||
|
4: chart: mychart3
|
||||||
|
5: - name: foo
|
||||||
|
6: chart: mychart1
|
||||||
|
7: needs:
|
||||||
|
8: - ns1/bar
|
||||||
|
9:
|
||||||
|
|
||||||
|
merged environment: &{default map[] map[]}
|
||||||
|
1 release(s) matching name=foo found in helmfile.yaml
|
||||||
|
|
||||||
|
err: release "default//foo" depends on "default/ns1/bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
|
||||||
|
`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "non-existent release in needs",
|
name: "non-existent release in needs",
|
||||||
loc: location(),
|
loc: location(),
|
||||||
|
|
@ -4225,18 +4288,18 @@ releases:
|
||||||
- name: foo
|
- name: foo
|
||||||
chart: mychart1
|
chart: mychart1
|
||||||
needs:
|
needs:
|
||||||
- bar
|
- ns1/bar
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
diffs: map[exectest.DiffKey]error{
|
diffs: map[exectest.DiffKey]error{
|
||||||
exectest.DiffKey{Name: "baz", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
exectest.DiffKey{Name: "bar", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
},
|
},
|
||||||
lists: map[exectest.ListKey]string{},
|
lists: map[exectest.ListKey]string{},
|
||||||
upgraded: []exectest.Release{},
|
upgraded: []exectest.Release{},
|
||||||
deleted: []exectest.Release{},
|
deleted: []exectest.Release{},
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
|
error: "in ./helmfile.yaml: release(s) \"default//foo\" depend(s) on an undefined release \"default/ns1/bar\". Perhaps you made a typo in \"needs\" or forgot defining a release named \"bar\" with appropriate \"namespace\" and \"kubeContext\"?",
|
||||||
log: `processing file "helmfile.yaml" in directory "."
|
log: `processing file "helmfile.yaml" in directory "."
|
||||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||||
first-pass uses: &{default map[] map[]}
|
first-pass uses: &{default map[] map[]}
|
||||||
|
|
@ -4249,7 +4312,7 @@ first-pass rendering output of "helmfile.yaml.part.0":
|
||||||
5: - name: foo
|
5: - name: foo
|
||||||
6: chart: mychart1
|
6: chart: mychart1
|
||||||
7: needs:
|
7: needs:
|
||||||
8: - bar
|
8: - ns1/bar
|
||||||
9:
|
9:
|
||||||
|
|
||||||
first-pass produced: &{default map[] map[]}
|
first-pass produced: &{default map[] map[]}
|
||||||
|
|
@ -4266,13 +4329,85 @@ second-pass rendering result of "helmfile.yaml.part.0":
|
||||||
5: - name: foo
|
5: - name: foo
|
||||||
6: chart: mychart1
|
6: chart: mychart1
|
||||||
7: needs:
|
7: needs:
|
||||||
8: - bar
|
8: - ns1/bar
|
||||||
9:
|
9:
|
||||||
|
|
||||||
merged environment: &{default map[] map[]}
|
merged environment: &{default map[] map[]}
|
||||||
2 release(s) found in helmfile.yaml
|
2 release(s) found in helmfile.yaml
|
||||||
|
|
||||||
err: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
|
err: release(s) "default//foo" depend(s) on an undefined release "default/ns1/bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "duplicate releases",
|
||||||
|
loc: location(),
|
||||||
|
files: map[string]string{
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
releases:
|
||||||
|
- name: bar
|
||||||
|
namespace: ns1
|
||||||
|
chart: mychart3
|
||||||
|
- name: foo
|
||||||
|
chart: mychart2
|
||||||
|
needs:
|
||||||
|
- ns1/bar
|
||||||
|
- name: foo
|
||||||
|
chart: mychart1
|
||||||
|
needs:
|
||||||
|
- ns1/bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
diffs: map[exectest.DiffKey]error{
|
||||||
|
exectest.DiffKey{Name: "bar", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
},
|
||||||
|
lists: map[exectest.ListKey]string{},
|
||||||
|
upgraded: []exectest.Release{},
|
||||||
|
deleted: []exectest.Release{},
|
||||||
|
concurrency: 1,
|
||||||
|
error: "in ./helmfile.yaml: found 2 duplicate releases with ID \"default//foo\"",
|
||||||
|
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: releases:
|
||||||
|
2: - name: bar
|
||||||
|
3: namespace: ns1
|
||||||
|
4: chart: mychart3
|
||||||
|
5: - name: foo
|
||||||
|
6: chart: mychart2
|
||||||
|
7: needs:
|
||||||
|
8: - ns1/bar
|
||||||
|
9: - name: foo
|
||||||
|
10: chart: mychart1
|
||||||
|
11: needs:
|
||||||
|
12: - ns1/bar
|
||||||
|
13:
|
||||||
|
|
||||||
|
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: bar
|
||||||
|
3: namespace: ns1
|
||||||
|
4: chart: mychart3
|
||||||
|
5: - name: foo
|
||||||
|
6: chart: mychart2
|
||||||
|
7: needs:
|
||||||
|
8: - ns1/bar
|
||||||
|
9: - name: foo
|
||||||
|
10: chart: mychart1
|
||||||
|
11: needs:
|
||||||
|
12: - ns1/bar
|
||||||
|
13:
|
||||||
|
|
||||||
|
merged environment: &{default map[] map[]}
|
||||||
|
err: found 2 duplicate releases with ID "default//foo"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1227,7 +1227,7 @@ releases:
|
||||||
upgraded: []exectest.Release{},
|
upgraded: []exectest.Release{},
|
||||||
deleted: []exectest.Release{},
|
deleted: []exectest.Release{},
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
error: `in ./helmfile.yaml: release "foo" depends on "bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
|
error: `in ./helmfile.yaml: release(s) "foo" depend(s) on an undefined release "bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?`,
|
||||||
log: `processing file "helmfile.yaml" in directory "."
|
log: `processing file "helmfile.yaml" in directory "."
|
||||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||||
first-pass uses: &{default map[] map[]}
|
first-pass uses: &{default map[] map[]}
|
||||||
|
|
@ -1263,7 +1263,7 @@ second-pass rendering result of "helmfile.yaml.part.0":
|
||||||
merged environment: &{default map[] map[]}
|
merged environment: &{default map[] map[]}
|
||||||
2 release(s) found in helmfile.yaml
|
2 release(s) found in helmfile.yaml
|
||||||
|
|
||||||
err: release "foo" depends on "bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
|
err: release(s) "foo" depend(s) on an undefined release "bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1313,6 +1313,68 @@ merged environment: &{default map[] map[]}
|
||||||
//
|
//
|
||||||
// error cases
|
// error cases
|
||||||
//
|
//
|
||||||
|
{
|
||||||
|
name: "unselected release in needs",
|
||||||
|
loc: location(),
|
||||||
|
flags: flags{skipNeeds: false},
|
||||||
|
files: map[string]string{
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
releases:
|
||||||
|
- name: bar
|
||||||
|
chart: mychart3
|
||||||
|
- name: foo
|
||||||
|
chart: mychart1
|
||||||
|
needs:
|
||||||
|
- bar
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
detailedExitcode: true,
|
||||||
|
selectors: []string{"name=foo"},
|
||||||
|
diffs: map[exectest.DiffKey]error{
|
||||||
|
exectest.DiffKey{Name: "bar", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||||
|
},
|
||||||
|
lists: map[exectest.ListKey]string{},
|
||||||
|
upgraded: []exectest.Release{},
|
||||||
|
deleted: []exectest.Release{},
|
||||||
|
concurrency: 1,
|
||||||
|
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
|
||||||
|
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: releases:
|
||||||
|
2: - name: bar
|
||||||
|
3: chart: mychart3
|
||||||
|
4: - name: foo
|
||||||
|
5: chart: mychart1
|
||||||
|
6: needs:
|
||||||
|
7: - bar
|
||||||
|
8:
|
||||||
|
|
||||||
|
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: bar
|
||||||
|
3: chart: mychart3
|
||||||
|
4: - name: foo
|
||||||
|
5: chart: mychart1
|
||||||
|
6: needs:
|
||||||
|
7: - bar
|
||||||
|
8:
|
||||||
|
|
||||||
|
merged environment: &{default map[] map[]}
|
||||||
|
1 release(s) matching name=foo found in helmfile.yaml
|
||||||
|
|
||||||
|
err: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
|
||||||
|
`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "non-existent release in needs",
|
name: "non-existent release in needs",
|
||||||
loc: location(),
|
loc: location(),
|
||||||
|
|
@ -1337,7 +1399,7 @@ releases:
|
||||||
upgraded: []exectest.Release{},
|
upgraded: []exectest.Release{},
|
||||||
deleted: []exectest.Release{},
|
deleted: []exectest.Release{},
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
|
error: `in ./helmfile.yaml: release(s) "default//foo" depend(s) on an undefined release "default//bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?`,
|
||||||
log: `processing file "helmfile.yaml" in directory "."
|
log: `processing file "helmfile.yaml" in directory "."
|
||||||
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
|
||||||
first-pass uses: &{default map[] map[]}
|
first-pass uses: &{default map[] map[]}
|
||||||
|
|
@ -1373,7 +1435,7 @@ second-pass rendering result of "helmfile.yaml.part.0":
|
||||||
merged environment: &{default map[] map[]}
|
merged environment: &{default map[] map[]}
|
||||||
2 release(s) found in helmfile.yaml
|
2 release(s) found in helmfile.yaml
|
||||||
|
|
||||||
err: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
|
err: release(s) "default//foo" depend(s) on an undefined release "default//bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1888,8 +1888,11 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, p := range preps {
|
for _, p := range preps {
|
||||||
if stdout, ok := outputs[ReleaseToID(p.release)]; ok {
|
id := ReleaseToID(p.release)
|
||||||
|
if stdout, ok := outputs[id]; ok {
|
||||||
fmt.Print(stdout.String())
|
fmt.Print(stdout.String())
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("missing output for release %s", id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,15 +150,12 @@ func GroupReleasesByDependency(releases []Release, opts PlanOptions) ([][]Releas
|
||||||
idToReleases[id] = append(idToReleases[id], r)
|
idToReleases[id] = append(idToReleases[id], r)
|
||||||
idToIndex[id] = i
|
idToIndex[id] = i
|
||||||
|
|
||||||
// Only compute dependencies from non-filtered releases
|
var needs []string
|
||||||
if !r.Filtered {
|
for i := 0; i < len(r.Needs); i++ {
|
||||||
var needs []string
|
n := r.Needs[i]
|
||||||
for i := 0; i < len(r.Needs); i++ {
|
needs = append(needs, n)
|
||||||
n := r.Needs[i]
|
|
||||||
needs = append(needs, n)
|
|
||||||
}
|
|
||||||
d.Add(id, dag.Dependencies(needs))
|
|
||||||
}
|
}
|
||||||
|
d.Add(id, dag.Dependencies(needs))
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids []string
|
var ids []string
|
||||||
|
|
@ -218,6 +215,22 @@ func GroupReleasesByDependency(releases []Release, opts PlanOptions) ([][]Releas
|
||||||
msgs[i] = msg
|
msgs[i] = msg
|
||||||
}
|
}
|
||||||
return nil, errors.New(msgs[0])
|
return nil, errors.New(msgs[0])
|
||||||
|
} else if ude, ok := err.(*dag.UndefinedDependencyError); ok {
|
||||||
|
var quotedReleaseNames []string
|
||||||
|
for _, d := range ude.Dependents {
|
||||||
|
quotedReleaseNames = append(quotedReleaseNames, fmt.Sprintf("%q", d))
|
||||||
|
}
|
||||||
|
|
||||||
|
idComponents := strings.Split(ude.UndefinedNode, "/")
|
||||||
|
name := idComponents[len(idComponents)-1]
|
||||||
|
humanReadableUndefinedReleaseInfo := fmt.Sprintf(`named %q with appropriate "namespace" and "kubeContext"`, name)
|
||||||
|
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
`release(s) %s depend(s) on an undefined release %q. Perhaps you made a typo in "needs" or forgot defining a release %s?`,
|
||||||
|
strings.Join(quotedReleaseNames, ", "),
|
||||||
|
ude.UndefinedNode,
|
||||||
|
humanReadableUndefinedReleaseInfo,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue