Add support for transitive dependencies. (#1983)
Co-authored-by: Peter Aichinger <petera@topdesk.com>
This commit is contained in:
		
							parent
							
								
									9a0ce53608
								
							
						
					
					
						commit
						77e6268bcb
					
				
							
								
								
									
										24
									
								
								main.go
								
								
								
								
							
							
						
						
									
										24
									
								
								main.go
								
								
								
								
							|  | @ -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
 | ||||
|  |  | |||
							
								
								
									
										126
									
								
								pkg/app/app.go
								
								
								
								
							
							
						
						
									
										126
									
								
								pkg/app/app.go
								
								
								
								
							|  | @ -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} | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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           | ||||
| 
 | ||||
| `, | ||||
| 		}) | ||||
| 	}) | ||||
|  |  | |||
|  | @ -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{ | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
| 		} | ||||
|  |  | |||
|  | @ -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} | ||||
| 		} | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue