Add support for transitive dependencies. (#1983)

Co-authored-by: Peter Aichinger <petera@topdesk.com>
This commit is contained in:
pjotre86
2021-10-20 17:55:08 +09:00
committed by GitHub
co-authored by Peter Aichinger
parent 9a0ce53608
commit 77e6268bcb
14 changed files with 583 additions and 177 deletions
+69 -57
View File
@@ -120,7 +120,7 @@ func (a *App) Deps(c DepsConfigProvider) error {
}
return
}, SetFilter(true))
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
func (a *App) Repos(c ReposConfigProvider) error {
@@ -132,7 +132,7 @@ func (a *App) Repos(c ReposConfigProvider) error {
}
return
}, SetFilter(true))
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
@@ -149,7 +149,7 @@ func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
}
return
}, SetFilter(true))
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
func (a *App) Diff(c DiffConfigProvider) error {
@@ -203,7 +203,7 @@ func (a *App) Diff(c DiffConfigProvider) error {
}
return matched, criticalErrs
})
}, false)
if err != nil {
return err
@@ -225,9 +225,6 @@ func (a *App) Diff(c DiffConfigProvider) error {
}
func (a *App) Template(c TemplateConfigProvider) error {
opts := []LoadOption{SetRetainValuesFiles(c.SkipCleanup())}
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
includeCRDs := c.IncludeCRDs()
@@ -249,7 +246,7 @@ func (a *App) Template(c TemplateConfigProvider) error {
}
return
}, opts...)
}, c.IncludeTransitiveNeeds())
}
func (a *App) WriteValues(c WriteValuesConfigProvider) error {
@@ -269,7 +266,7 @@ func (a *App) WriteValues(c WriteValuesConfigProvider) error {
}
return
}, SetFilter(true))
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
type MultiError struct {
@@ -322,7 +319,7 @@ func (a *App) Lint(c LintConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
if err != nil {
return err
@@ -350,7 +347,7 @@ func (a *App) Fetch(c FetchConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
}
func (a *App) Sync(c SyncConfigProvider) error {
@@ -358,11 +355,12 @@ func (a *App) Sync(c SyncConfigProvider) error {
includeCRDs := !c.SkipCRDs()
prepErr := run.withPreparedCharts("sync", state.ChartPrepareOptions{
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Wait: c.Wait(),
WaitForJobs: c.WaitForJobs(),
IncludeCRDs: &includeCRDs,
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Wait: c.Wait(),
WaitForJobs: c.WaitForJobs(),
IncludeCRDs: &includeCRDs,
IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(),
}, func() {
ok, errs = a.sync(run, c)
})
@@ -372,7 +370,7 @@ func (a *App) Sync(c SyncConfigProvider) error {
}
return
})
}, c.IncludeTransitiveNeeds())
}
func (a *App) Apply(c ApplyConfigProvider) error {
@@ -410,7 +408,7 @@ func (a *App) Apply(c ApplyConfigProvider) error {
}
return
}, opts...)
}, c.IncludeTransitiveNeeds(), opts...)
if err != nil {
return err
@@ -439,7 +437,7 @@ func (a *App) Status(c StatusesConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
}
func (a *App) Delete(c DeleteConfigProvider) error {
@@ -456,7 +454,7 @@ func (a *App) Delete(c DeleteConfigProvider) error {
}
return
}, SetReverse(true))
}, false, SetReverse(true))
}
func (a *App) Destroy(c DestroyConfigProvider) error {
@@ -473,7 +471,7 @@ func (a *App) Destroy(c DestroyConfigProvider) error {
}
return
}, SetReverse(true))
}, false, SetReverse(true))
}
func (a *App) Test(c TestConfigProvider) error {
@@ -496,7 +494,7 @@ func (a *App) Test(c TestConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
}
func (a *App) PrintState(c StateConfigProvider) error {
@@ -543,7 +541,7 @@ func (a *App) PrintState(c StateConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
}
func (a *App) ListReleases(c ListConfigProvider) error {
@@ -594,7 +592,7 @@ func (a *App) ListReleases(c ListConfigProvider) error {
}
return
}, SetFilter(true))
}, false, SetFilter(true))
if err != nil {
return err
@@ -883,14 +881,14 @@ var (
}
)
func (a *App) ForEachState(do func(*Run) (bool, []error), o ...LoadOption) error {
func (a *App) ForEachState(do func(*Run) (bool, []error), includeTransitiveNeeds bool, o ...LoadOption) error {
ctx := NewContext()
err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState) (bool, []error) {
helm := a.getHelm(st)
run := NewRun(st, helm, ctx)
return do(run)
}, o...)
}, includeTransitiveNeeds, o...)
return err
}
@@ -966,7 +964,7 @@ type Opts struct {
DAGEnabled bool
}
func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converge func(*state.HelmState) (bool, []error), opt ...LoadOption) error {
func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converge func(*state.HelmState) (bool, []error), includeTransitiveNeeds bool, opt ...LoadOption) error {
opts := LoadOpts{
Selectors: a.Selectors,
}
@@ -1004,16 +1002,17 @@ func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converg
return processFilteredReleases(st, a.getHelm(st), func(st *state.HelmState) []error {
_, err := converge(st)
return err
})
},
includeTransitiveNeeds)
}
}
return a.visitStates(fileOrDir, opts, f)
}
func processFilteredReleases(st *state.HelmState, helm helmexec.Interface, converge func(st *state.HelmState) []error) (bool, []error) {
func processFilteredReleases(st *state.HelmState, helm helmexec.Interface, converge func(st *state.HelmState) []error, includeTransitiveNeeds bool) (bool, []error) {
if len(st.Selectors) > 0 {
err := st.FilterReleases()
err := st.FilterReleases(includeTransitiveNeeds)
if err != nil {
return false, []error{err}
}
@@ -1066,11 +1065,11 @@ func checkDuplicates(helm helmexec.Interface, st *state.HelmState, releases []st
return nil
}
func (a *App) Wrap(converge func(*state.HelmState, helmexec.Interface) []error) func(st *state.HelmState, helm helmexec.Interface) (bool, []error) {
return func(st *state.HelmState, helm helmexec.Interface) (bool, []error) {
func (a *App) Wrap(converge func(*state.HelmState, helmexec.Interface) []error) func(st *state.HelmState, helm helmexec.Interface, includeTransitiveNeeds bool) (bool, []error) {
return func(st *state.HelmState, helm helmexec.Interface, includeTransitiveNeeds bool) (bool, []error) {
return processFilteredReleases(st, helm, func(st *state.HelmState) []error {
return converge(st, helm)
})
}, includeTransitiveNeeds)
}
}
@@ -1144,8 +1143,8 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
return files, nil
}
func (a *App) getSelectedReleases(r *Run) ([]state.ReleaseSpec, []state.ReleaseSpec, error) {
selected, err := r.state.GetSelectedReleasesWithOverrides()
func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state.ReleaseSpec, []state.ReleaseSpec, error) {
selected, err := r.state.GetSelectedReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return nil, nil, err
}
@@ -1159,12 +1158,7 @@ func (a *App) getSelectedReleases(r *Run) ([]state.ReleaseSpec, []state.ReleaseS
needed := map[string]struct{}{}
for _, r := range selected {
for _, id := range r.Needs {
// Avoids duplicating a release that is selected AND also needed by another selected release
if _, ok := selectedIds[id]; !ok {
needed[id] = struct{}{}
}
}
collectNeeds(r, selectedIds, needed, allReleases, includeTransitiveNeeds)
}
var releases []state.ReleaseSpec
@@ -1192,11 +1186,29 @@ func (a *App) getSelectedReleases(r *Run) ([]state.ReleaseSpec, []state.ReleaseS
return selected, releases, 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 {
needed[id] = struct{}{}
if includeTransitiveNeeds {
releaseParts := strings.Split(id, "/")
releaseName := releaseParts[len(releaseParts)-1]
for _, r := range allReleases {
if r.Name == releaseName {
collectNeeds(r, selectedIds, needed, allReleases, includeTransitiveNeeds)
}
}
}
}
}
}
func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
st := r.state
helm := r.helm
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds())
if err != nil {
return false, false, []error{err}
}
@@ -1210,7 +1222,7 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
// See https://github.com/roboll/helmfile/issues/1818 for more context.
st.Releases = selectedAndNeededReleases
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds()})
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()})
if err != nil {
return false, false, []error{err}
}
@@ -1320,7 +1332,7 @@ Do you really want to apply?
// We upgrade releases by traversing the DAG
if len(releasesToBeUpdated) > 0 {
_, updateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, Reverse: false, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
_, 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
for _, r := range subst.Releases {
@@ -1357,7 +1369,7 @@ func (a *App) delete(r *Run, purge bool, c DestroyConfigProvider) (bool, []error
affectedReleases := state.AffectedReleases{}
toSync, _, err := a.getSelectedReleases(r)
toSync, _, err := a.getSelectedReleases(r, false)
if err != nil {
return false, []error{err}
}
@@ -1429,7 +1441,7 @@ Do you really want to delete?
func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) {
st := r.state
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, false)
if err != nil {
return nil, false, false, []error{err}
}
@@ -1450,7 +1462,7 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error)
st.Releases = selectedAndNeededReleases
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds()})
plan, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, SkipNeeds: c.SkipNeeds(), IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: false})
if err != nil {
return nil, false, false, []error{err}
}
@@ -1485,7 +1497,7 @@ func (a *App) lint(r *Run, c LintConfigProvider) (bool, []error, []error) {
allReleases := st.GetReleasesWithOverrides()
selectedReleases, _, err := a.getSelectedReleases(r)
selectedReleases, _, err := a.getSelectedReleases(r, false)
if err != nil {
return false, nil, []error{err}
}
@@ -1553,7 +1565,7 @@ func (a *App) status(r *Run, c StatusesConfigProvider) (bool, []error) {
allReleases := st.GetReleasesWithOverrides()
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, false)
if err != nil {
return false, []error{err}
}
@@ -1603,7 +1615,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) {
st := r.state
helm := r.helm
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds())
if err != nil {
return false, []error{err}
}
@@ -1617,7 +1629,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) {
// See https://github.com/roboll/helmfile/issues/1818 for more context.
st.Releases = selectedAndNeededReleases
batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: c.IncludeNeeds(), SkipNeeds: c.SkipNeeds()})
batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(), SkipNeeds: c.SkipNeeds()})
if err != nil {
return false, []error{err}
}
@@ -1727,7 +1739,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) {
}
if len(releasesToUpdate) > 0 {
_, syncErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
_, syncErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toUpdate, SkipNeeds: true, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
var rs []state.ReleaseSpec
for _, r := range subst.Releases {
@@ -1759,7 +1771,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
st := r.state
helm := r.helm
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds())
if err != nil {
return false, []error{err}
}
@@ -1773,7 +1785,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
// See https://github.com/roboll/helmfile/issues/1818 for more context.
st.Releases = selectedAndNeededReleases
batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: c.IncludeNeeds(), SkipNeeds: !c.IncludeNeeds()})
batches, err := st.PlanReleases(state.PlanOptions{Reverse: false, SelectedReleases: selectedReleases, IncludeNeeds: c.IncludeNeeds(), IncludeTransitiveNeeds: c.IncludeTransitiveNeeds(), SkipNeeds: !c.IncludeNeeds()})
if err != nil {
return false, []error{err}
}
@@ -1813,7 +1825,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
}
if len(toRender) > 0 {
_, templateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toRender, Reverse: false, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
_, templateErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toRender, Reverse: false, SkipNeeds: true, IncludeTransitiveNeeds: c.IncludeTransitiveNeeds()}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
opts := &state.TemplateOpts{
Set: c.Set(),
IncludeCRDs: c.IncludeCRDs(),
@@ -1837,7 +1849,7 @@ func (a *App) test(r *Run, c TestConfigProvider) []error {
st := r.state
toTest, _, err := a.getSelectedReleases(r)
toTest, _, err := a.getSelectedReleases(r, false)
if err != nil {
return []error{err}
}
@@ -1859,7 +1871,7 @@ func (a *App) writeValues(r *Run, c WriteValuesConfigProvider) (bool, []error) {
st := r.state
helm := r.helm
toRender, _, err := a.getSelectedReleases(r)
toRender, _, err := a.getSelectedReleases(r, false)
if err != nil {
return false, []error{err}
}
+9 -7
View File
@@ -17,8 +17,9 @@ import (
func TestApply_3(t *testing.T) {
type fields struct {
skipNeeds bool
includeNeeds bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
}
type testcase struct {
@@ -109,11 +110,12 @@ func TestApply_3(t *testing.T) {
syncErr := app.Apply(applyConfig{
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
includeTransitiveNeeds: tc.fields.includeTransitiveNeeds,
})
var gotErr string
+131 -7
View File
@@ -17,8 +17,9 @@ import (
func TestApply_2(t *testing.T) {
type fields struct {
skipNeeds bool
includeNeeds bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
}
type testcase struct {
@@ -109,11 +110,12 @@ func TestApply_2(t *testing.T) {
syncErr := app.Apply(applyConfig{
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
includeTransitiveNeeds: tc.fields.includeTransitiveNeeds,
})
var gotErr string
@@ -960,6 +962,128 @@ NAME CHART VERSION
external-secrets incubator/raw
my-release incubator/raw
`,
})
})
t.Run("include-transitive-needs=true", func(t *testing.T) {
check(t, testcase{
fields: fields{
skipNeeds: false,
includeNeeds: true,
includeTransitiveNeeds: true,
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
- name: serviceA
chart: my/chart
needs:
- serviceB
- name: serviceB
chart: my/chart
needs:
- serviceC
- name: serviceC
chart: my/chart
- name: serviceD
chart: my/chart
`,
},
selectors: []string{"name=serviceA"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
exectest.DiffKey{Name: "serviceA", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
exectest.DiffKey{Name: "serviceB", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
exectest.DiffKey{Name: "serviceC", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
// 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: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
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: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
merged environment: &{default map[] map[]}
3 release(s) matching name=serviceA found in helmfile.yaml
Affected releases are:
serviceA (my/chart) UPDATED
serviceB (my/chart) UPDATED
serviceC (my/chart) UPDATED
processing 3 groups of releases in this order:
GROUP RELEASES
1 default//serviceC
2 default//serviceB
3 default//serviceA
processing releases in group 1/3: default//serviceC
getting deployed release version failed:unexpected list key: {^serviceC$ --kube-contextdefault--deleting--deployed--failed--pending}
processing releases in group 2/3: default//serviceB
getting deployed release version failed:unexpected list key: {^serviceB$ --kube-contextdefault--deleting--deployed--failed--pending}
processing releases in group 3/3: default//serviceA
getting deployed release version failed:unexpected list key: {^serviceA$ --kube-contextdefault--deleting--deployed--failed--pending}
UPDATED RELEASES:
NAME CHART VERSION
serviceC my/chart
serviceB my/chart
serviceA my/chart
`,
})
})
+131 -11
View File
@@ -17,8 +17,9 @@ import (
func TestSync(t *testing.T) {
type fields struct {
skipNeeds bool
includeNeeds bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
}
type testcase struct {
@@ -107,11 +108,12 @@ func TestSync(t *testing.T) {
syncErr := app.Sync(applyConfig{
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
concurrency: tc.concurrency,
logger: logger,
skipDiffOnInstall: tc.skipDiffOnInstall,
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
includeTransitiveNeeds: tc.fields.includeTransitiveNeeds,
})
var gotErr string
@@ -412,6 +414,122 @@ kubernetes-external-secrets incubator/raw
external-secrets incubator/raw
my-release incubator/raw
`,
})
})
t.Run("include-transitive-needs=true", func(t *testing.T) {
check(t, testcase{
fields: fields{
skipNeeds: false,
includeTransitiveNeeds: true,
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
- name: serviceA
chart: my/chart
needs:
- serviceB
- name: serviceB
chart: my/chart
needs:
- serviceC
- name: serviceC
chart: my/chart
- name: serviceD
chart: my/chart
`,
},
selectors: []string{"name=serviceA"},
upgraded: []exectest.Release{},
// 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: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
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: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
merged environment: &{default map[] map[]}
3 release(s) matching name=serviceA found in helmfile.yaml
Affected releases are:
serviceA (my/chart) UPDATED
serviceB (my/chart) UPDATED
serviceC (my/chart) UPDATED
processing 3 groups of releases in this order:
GROUP RELEASES
1 default//serviceC
2 default//serviceB
3 default//serviceA
processing releases in group 1/3: default//serviceC
getting deployed release version failed:unexpected list key: {^serviceC$ --kube-contextdefault--deleting--deployed--failed--pending}
processing releases in group 2/3: default//serviceB
getting deployed release version failed:unexpected list key: {^serviceB$ --kube-contextdefault--deleting--deployed--failed--pending}
processing releases in group 3/3: default//serviceA
getting deployed release version failed:unexpected list key: {^serviceA$ --kube-contextdefault--deleting--deployed--failed--pending}
UPDATED RELEASES:
NAME CHART VERSION
serviceC my/chart
serviceB my/chart
serviceA my/chart
`,
})
})
@@ -419,8 +537,9 @@ my-release incubator/raw
t.Run("skip-needs=false include-needs=true with installed but disabled release", func(t *testing.T) {
check(t, testcase{
fields: fields{
skipNeeds: false,
includeNeeds: true,
skipNeeds: false,
includeNeeds: true,
includeTransitiveNeeds: false,
},
error: ``,
files: map[string]string{
@@ -561,8 +680,9 @@ kubernetes-external-secrets
t.Run("skip-needs=false include-needs=true with not installed and disabled release", func(t *testing.T) {
check(t, testcase{
fields: fields{
skipNeeds: false,
includeNeeds: true,
skipNeeds: false,
includeTransitiveNeeds: false,
includeNeeds: true,
},
error: ``,
files: map[string]string{
+65 -28
View File
@@ -106,6 +106,7 @@ releases:
err := app.ForEachState(
noop,
false,
SetFilter(true),
)
if err != nil {
@@ -157,6 +158,7 @@ BAZ: 4
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if err != nil {
@@ -198,6 +200,7 @@ releases:
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if err == nil {
@@ -242,6 +245,7 @@ releases:
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if err != nil {
@@ -293,6 +297,7 @@ releases:
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if testcase.expectErr && err == nil {
@@ -359,6 +364,7 @@ releases:
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if testcase.expectErr && err == nil {
@@ -413,6 +419,7 @@ releases:
err := app.ForEachState(
Noop,
false,
SetFilter(true),
)
if testcase.expectErr && err == nil {
@@ -531,6 +538,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if testcase.expectErr {
@@ -774,6 +782,7 @@ func runFilterSubHelmFilesTests(testcases []struct {
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if testcase.expectErr {
@@ -869,6 +878,7 @@ tillerNs: INLINE_TILLER_NS_2
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -970,6 +980,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetReverse(testcase.reverse),
SetFilter(true),
)
@@ -1035,6 +1046,7 @@ bar: "bar1"
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if err != nil {
@@ -1157,6 +1169,7 @@ x:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if err != nil {
@@ -1209,6 +1222,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if err != nil {
@@ -1266,6 +1280,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if err != nil {
@@ -1316,6 +1331,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -1364,6 +1380,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -1407,6 +1424,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -1450,6 +1468,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -1497,6 +1516,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
@@ -2246,8 +2266,9 @@ type configImpl struct {
skipCRDs bool
skipDeps bool
skipNeeds bool
includeNeeds bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
}
func (a configImpl) Selectors() []string {
@@ -2290,6 +2311,10 @@ func (c configImpl) IncludeNeeds() bool {
return c.includeNeeds
}
func (c configImpl) IncludeTransitiveNeeds() bool {
return c.includeTransitiveNeeds
}
func (c configImpl) OutputDir() string {
return "output/subdir"
}
@@ -2315,30 +2340,31 @@ func (c configImpl) Output() string {
}
type applyConfig struct {
args string
values []string
retainValuesFiles bool
set []string
validate bool
skipCleanup bool
skipCRDs bool
skipDeps bool
skipNeeds bool
includeNeeds bool
includeTests bool
suppressSecrets bool
showSecrets bool
suppressDiff bool
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
interactive bool
skipDiffOnInstall bool
logger *zap.SugaredLogger
wait bool
waitForJobs bool
args string
values []string
retainValuesFiles bool
set []string
validate bool
skipCleanup bool
skipCRDs bool
skipDeps bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
includeTests bool
suppressSecrets bool
showSecrets bool
suppressDiff bool
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
interactive bool
skipDiffOnInstall bool
logger *zap.SugaredLogger
wait bool
waitForJobs bool
}
func (a applyConfig) Args() string {
@@ -2385,6 +2411,10 @@ func (c applyConfig) IncludeNeeds() bool {
return c.includeNeeds
}
func (c applyConfig) IncludeTransitiveNeeds() bool {
return c.includeTransitiveNeeds
}
func (a applyConfig) IncludeTests() bool {
return a.includeTests
}
@@ -2438,13 +2468,18 @@ func (a applyConfig) SkipDiffOnInstall() bool {
}
type depsConfig struct {
skipRepos bool
skipRepos bool
includeTransitiveNeeds bool
}
func (d depsConfig) SkipRepos() bool {
return d.skipRepos
}
func (d depsConfig) IncludeTransitiveNeeds() bool {
return d.includeTransitiveNeeds
}
func (d depsConfig) Args() string {
return ""
}
@@ -4482,7 +4517,8 @@ See https://github.com/roboll/helmfile/issues/878 for more information.
}, tc.files)
depsErr := app.Deps(depsConfig{
skipRepos: false,
skipRepos: false,
includeTransitiveNeeds: false,
})
if tc.error == "" && depsErr != nil {
@@ -4777,6 +4813,7 @@ releases:
err := app.ForEachState(
collectReleases,
false,
SetFilter(true),
)
if err != nil {
+7
View File
@@ -23,15 +23,18 @@ type DeprecatedChartsConfigProvider interface {
concurrencyConfig
loggingConfig
IncludeTransitiveNeeds() bool
}
type DepsConfigProvider interface {
Args() string
SkipRepos() bool
IncludeTransitiveNeeds() bool
}
type ReposConfigProvider interface {
Args() string
IncludeTransitiveNeeds() bool
}
type ApplyConfigProvider interface {
@@ -63,6 +66,7 @@ type ApplyConfigProvider interface {
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
concurrencyConfig
interactive
@@ -81,6 +85,7 @@ type SyncConfigProvider interface {
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
concurrencyConfig
loggingConfig
@@ -174,6 +179,7 @@ type TemplateConfigProvider interface {
OutputDir() string
IncludeCRDs() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
concurrencyConfig
}
@@ -183,6 +189,7 @@ type WriteValuesConfigProvider interface {
Set() []string
OutputFileTemplate() string
SkipDeps() bool
IncludeTransitiveNeeds() bool
}
type StatusesConfigProvider interface {
+3 -2
View File
@@ -103,8 +103,9 @@ func TestDestroy_2(t *testing.T) {
destroyErr := app.Destroy(destroyConfig{
// if we check log output, concurrency must be 1. otherwise the test becomes non-deterministic.
concurrency: tc.concurrency,
logger: logger,
concurrency: tc.concurrency,
logger: logger,
includeTransitiveNeeds: false,
})
if tc.error == "" && destroyErr != nil {
+10 -5
View File
@@ -23,11 +23,12 @@ const (
)
type destroyConfig struct {
args string
concurrency int
interactive bool
skipDeps bool
logger *zap.SugaredLogger
args string
concurrency int
interactive bool
skipDeps bool
logger *zap.SugaredLogger
includeTransitiveNeeds bool
}
func (d destroyConfig) Args() string {
@@ -50,6 +51,10 @@ func (d destroyConfig) SkipDeps() bool {
return d.skipDeps
}
func (d destroyConfig) IncludeTransitiveNeeds() bool {
return d.includeTransitiveNeeds
}
func TestDestroy(t *testing.T) {
type testcase struct {
helm3 bool
+1 -1
View File
@@ -98,7 +98,7 @@ func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepare
func (r *Run) Deps(c DepsConfigProvider) []error {
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
return r.state.UpdateDeps(r.helm)
return r.state.UpdateDeps(r.helm, c.IncludeTransitiveNeeds())
}
func (r *Run) Repos(c ReposConfigProvider) error {