Add support for transitive dependencies. (#1983)

Co-authored-by: Peter Aichinger <petera@topdesk.com>
This commit is contained in:
pjotre86 2021-10-20 10:55:08 +02:00 committed by GitHub
parent 9a0ce53608
commit 77e6268bcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 583 additions and 177 deletions

24
main.go
View File

@ -201,7 +201,7 @@ func main() {
},
cli.BoolTFlag{
Name: "skip-needs",
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs is not provided`,
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`,
},
cli.BoolFlag{
Name: "include-needs",
@ -286,6 +286,10 @@ func main() {
Name: "include-needs",
Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`,
},
cli.BoolFlag{
Name: "include-transitive-needs",
Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`,
},
cli.BoolFlag{
Name: "skip-deps",
Usage: `skip running "helm repo update" and "helm dependency build"`,
@ -414,12 +418,16 @@ func main() {
},
cli.BoolTFlag{
Name: "skip-needs",
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs is not provided`,
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`,
},
cli.BoolFlag{
Name: "include-needs",
Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`,
},
cli.BoolFlag{
Name: "include-transitive-needs",
Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`,
},
cli.BoolFlag{
Name: "wait",
Usage: `Override helmDefaults.wait setting "helm upgrade --install --wait"`,
@ -487,12 +495,16 @@ func main() {
},
cli.BoolTFlag{
Name: "skip-needs",
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs is not provided`,
Usage: `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`,
},
cli.BoolFlag{
Name: "include-needs",
Usage: `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when when --selector/-l flag is not provided`,
},
cli.BoolFlag{
Name: "include-transitive-needs",
Usage: `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`,
},
cli.BoolFlag{
Name: "skip-diff-on-install",
Usage: "Skips running helm-diff on releases being newly installed on this apply. Useful when the release manifests are too huge to be reviewed, or it's too time-consuming to diff at all",
@ -782,7 +794,11 @@ func (c configImpl) SkipNeeds() bool {
}
func (c configImpl) IncludeNeeds() bool {
return c.c.Bool("include-needs")
return c.c.Bool("include-needs") || c.IncludeTransitiveNeeds()
}
func (c configImpl) IncludeTransitiveNeeds() bool {
return c.c.Bool("include-transitive-needs")
}
// DiffConfig

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}
}

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

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
`,
})
})

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{

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 {

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 {

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 {

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

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 {

View File

@ -1,8 +1,9 @@
package state
import (
"github.com/google/go-cmp/cmp"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestSelectReleasesWithOverrides(t *testing.T) {
@ -79,7 +80,76 @@ func TestSelectReleasesWithOverrides(t *testing.T) {
for _, tc := range testcases {
state.Selectors = tc.selector
rs, err := state.GetSelectedReleasesWithOverrides()
rs, err := state.GetSelectedReleasesWithOverrides(false)
if err != nil {
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
}
var got []string
for _, r := range rs {
got = append(got, r.Name)
}
if d := cmp.Diff(tc.want, got); d != "" {
t.Errorf("%s %s: %s", tc.selector, tc.subject, d)
}
}
}
func TestSelectReleasesWithOverridesWithIncludedTransitives(t *testing.T) {
type testcase struct {
subject string
selector []string
want []string
includeTransitiveNeeds bool
}
testcases := []testcase{
{
subject: "include transitives",
selector: []string{"name=serviceA"},
want: []string{"serviceA"},
includeTransitiveNeeds: false,
},
{
subject: "include transitives",
selector: []string{"name=serviceA"},
want: []string{"serviceA", "serviceB", "serviceC"},
includeTransitiveNeeds: true,
},
}
example := []byte(`releases:
- name: serviceA
namespace: default
chart: stable/testchart
needs:
- serviceB
- name: serviceB
namespace: default
chart: stable/testchart
needs:
- serviceC
- name: serviceC
namespace: default
chart: stable/testchart
- name: serviceD
namespace: default
chart: stable/testchart
`)
state := stateTestEnv{
Files: map[string]string{
"/helmfile.yaml": string(example),
},
WorkDir: "/",
}.MustLoadState(t, "/helmfile.yaml", "default")
for _, tc := range testcases {
state.Selectors = tc.selector
rs, err := state.GetSelectedReleasesWithOverrides(tc.includeTransitiveNeeds)
if err != nil {
t.Fatalf("%s %s: %v", tc.selector, tc.subject, err)
}

View File

@ -398,36 +398,6 @@ type RepoUpdater interface {
RegistryLogin(name string, username string, password string) error
}
// getRepositoriesToSync returns the names of repositories to be updated
func (st *HelmState) getRepositoriesToSync() (map[string]bool, error) {
releases, err := st.GetSelectedReleasesWithOverrides()
if err != nil {
return nil, err
}
repositoriesToUpdate := map[string]bool{}
if len(releases) == 0 {
for _, repo := range st.Repositories {
repositoriesToUpdate[repo.Name] = true
}
return repositoriesToUpdate, nil
}
for _, release := range releases {
if release.Installed == nil || *release.Installed {
chart := strings.Split(release.Chart, "/")
if len(chart) == 1 {
continue
}
repositoriesToUpdate[chart[0]] = true
}
}
return repositoriesToUpdate, nil
}
func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]string, error) {
var updated []string
@ -968,11 +938,12 @@ type ChartPrepareOptions struct {
SkipCleanup bool
// Validate is a helm-3-only option. When it is set to true, it configures chartify to pass --validate to helm-template run by it.
// It's required when one of your chart relies on Capabilities.APIVersions in a template
Validate bool
IncludeCRDs *bool
Wait bool
WaitForJobs bool
OutputDir string
Validate bool
IncludeCRDs *bool
Wait bool
WaitForJobs bool
OutputDir string
IncludeTransitiveNeeds bool
}
type chartPrepareResult struct {
@ -1026,7 +997,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
// This and releasesNeedCharts ensures that we run operations like helm-dep-build and prepare-hook calls only on
// releases that are (1) selected by the selectors and (2) to be installed.
selected, err = st.GetSelectedReleasesWithOverrides()
selected, err = st.GetSelectedReleasesWithOverrides(opts.IncludeTransitiveNeeds)
if err != nil {
return nil, []error{err}
}
@ -2047,16 +2018,16 @@ func (st *HelmState) GetReleasesWithOverrides() []ReleaseSpec {
return rs
}
func (st *HelmState) SelectReleasesWithOverrides() ([]Release, error) {
func (st *HelmState) SelectReleasesWithOverrides(includeTransitiveNeeds bool) ([]Release, error) {
values := st.Values()
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, st.CommonLabels, values)
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, st.CommonLabels, values, includeTransitiveNeeds)
if err != nil {
return nil, err
}
return rs, nil
}
func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabels map[string]string, values map[string]interface{}) ([]Release, error) {
func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabels map[string]string, values map[string]interface{}, includeTransitiveNeeds bool) ([]Release, error) {
var filteredReleases []Release
filters := []ReleaseFilter{}
for _, label := range selectors {
@ -2113,12 +2084,52 @@ func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabe
}
filteredReleases = append(filteredReleases, res)
}
if includeTransitiveNeeds {
unmarkNeedsAndTransitives(filteredReleases, releases)
}
return filteredReleases, nil
}
func (st *HelmState) GetSelectedReleasesWithOverrides() ([]ReleaseSpec, error) {
filteredReleases, err := st.SelectReleasesWithOverrides()
func unmarkNeedsAndTransitives(filteredReleases []Release, allReleases []ReleaseSpec) {
needsWithTranstives := collectAllNeedsWithTransitives(filteredReleases, allReleases)
unmarkReleases(needsWithTranstives, filteredReleases)
}
func collectAllNeedsWithTransitives(filteredReleases []Release, allReleases []ReleaseSpec) map[string]struct{} {
needsWithTranstives := map[string]struct{}{}
for _, r := range filteredReleases {
if !r.Filtered {
collectNeedsWithTransitives(r.ReleaseSpec, allReleases, needsWithTranstives)
}
}
return needsWithTranstives
}
func unmarkReleases(toUnmark map[string]struct{}, releases []Release) {
for i, r := range releases {
if _, ok := toUnmark[ReleaseToID(&r.ReleaseSpec)]; ok {
releases[i].Filtered = false
}
}
}
func collectNeedsWithTransitives(release ReleaseSpec, allReleases []ReleaseSpec, needsWithTranstives map[string]struct{}) {
for _, id := range release.Needs {
if _, exists := needsWithTranstives[id]; !exists {
needsWithTranstives[id] = struct{}{}
releaseParts := strings.Split(id, "/")
releaseName := releaseParts[len(releaseParts)-1]
for _, r := range allReleases {
if r.Name == releaseName {
collectNeedsWithTransitives(r, allReleases, needsWithTranstives)
}
}
}
}
}
func (st *HelmState) GetSelectedReleasesWithOverrides(includeTransitiveNeeds bool) ([]ReleaseSpec, error) {
filteredReleases, err := st.SelectReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return nil, err
}
@ -2133,8 +2144,8 @@ func (st *HelmState) GetSelectedReleasesWithOverrides() ([]ReleaseSpec, error) {
}
// 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.GetSelectedReleasesWithOverrides()
func (st *HelmState) FilterReleases(includeTransitiveNeeds bool) error {
releases, err := st.GetSelectedReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return err
}
@ -2209,7 +2220,7 @@ func (st *HelmState) ResolveDeps() (*HelmState, error) {
}
// UpdateDeps wrapper for updating dependencies on the releases
func (st *HelmState) UpdateDeps(helm helmexec.Interface) []error {
func (st *HelmState) UpdateDeps(helm helmexec.Interface, includeTransitiveNeeds bool) []error {
var selected []ReleaseSpec
if len(st.Selectors) > 0 {
@ -2217,7 +2228,7 @@ func (st *HelmState) UpdateDeps(helm helmexec.Interface) []error {
// This and releasesNeedCharts ensures that we run operations like helm-dep-build and prepare-hook calls only on
// releases that are (1) selected by the selectors and (2) to be installed.
selected, err = st.GetSelectedReleasesWithOverrides()
selected, err = st.GetSelectedReleasesWithOverrides(includeTransitiveNeeds)
if err != nil {
return []error{err}
}

View File

@ -100,14 +100,15 @@ func (st *HelmState) iterateOnReleases(helm helmexec.Interface, concurrency int,
}
type PlanOptions struct {
Reverse bool
IncludeNeeds bool
SkipNeeds bool
SelectedReleases []ReleaseSpec
Reverse bool
IncludeNeeds bool
IncludeTransitiveNeeds bool
SkipNeeds bool
SelectedReleases []ReleaseSpec
}
func (st *HelmState) PlanReleases(opts PlanOptions) ([][]Release, error) {
marked, err := st.SelectReleasesWithOverrides()
marked, err := st.SelectReleasesWithOverrides(opts.IncludeTransitiveNeeds)
if err != nil {
return nil, err
}

View File

@ -1848,7 +1848,7 @@ generated: 2019-05-16T15:42:45.50486+09:00
})
fs.Cwd = basePath
state = injectFs(state, fs)
errs := state.UpdateDeps(helm)
errs := state.UpdateDeps(helm, false)
want := []string{"/example", "./example", generatedDir}
if !reflect.DeepEqual(helm.Charts, want) {
@ -2141,7 +2141,7 @@ func TestHelmState_NoReleaseMatched(t *testing.T) {
RenderedValues: map[string]interface{}{},
}
state.Selectors = []string{tt.labels}
errs := state.FilterReleases()
errs := state.FilterReleases(false)
if (errs != nil) != tt.wantErr {
t.Errorf("ReleaseStatuses() for %s error = %v, wantErr %v", tt.name, errs, tt.wantErr)
return