diff --git a/pkg/app/app.go b/pkg/app/app.go index 5b0e6290..fcc74386 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -216,7 +216,9 @@ func (a *App) Diff(c DiffConfigProvider) error { func (a *App) Template(c TemplateConfigProvider) error { return a.ForEachState(func(run *Run) (ok bool, errs []error) { - prepErr := run.withPreparedCharts(true, c.SkipDeps(), "template", func() { + // `helm template` in helm v2 does not support local chart. + // So, we set forceDownload=true for helm v2 only + prepErr := run.withPreparedCharts(!run.helm.IsHelm3(), c.SkipDeps(), "template", func() { ok, errs = a.template(run, c) }) @@ -230,6 +232,7 @@ func (a *App) Template(c TemplateConfigProvider) error { func (a *App) Lint(c LintConfigProvider) error { return a.ForEachState(func(run *Run) (_ bool, errs []error) { + // `helm lint` on helm v2 and v3 does not support remote charts, that we need to set `forceDownload=true` here prepErr := run.withPreparedCharts(true, c.SkipDeps(), "lint", func() { errs = run.Lint(c) }) @@ -970,15 +973,6 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) { // on running various helm commands on unnecessary releases st.Releases = toApply - if !c.SkipDeps() { - if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 { - return false, false, errs - } - } - if errs := st.PrepareReleases(helm, "apply"); errs != nil && len(errs) > 0 { - return false, false, errs - } - // helm must be 2.11+ and helm-diff should be provided `--detailed-exitcode` in order for `helmfile apply` to work properly detailedExitCode := true @@ -1157,15 +1151,6 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) { // on running various helm commands on unnecessary releases st.Releases = toSync - if !c.SkipDeps() { - if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 { - return false, errs - } - } - if errs := st.PrepareReleases(helm, "sync"); errs != nil && len(errs) > 0 { - return false, errs - } - toDelete, err := st.DetectReleasesToBeDeletedForSync(helm, toSync) if err != nil { return false, []error{err} @@ -1279,15 +1264,6 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) { // on running various helm commands on unnecessary releases st.Releases = toRender - if !c.SkipDeps() { - if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 { - return false, errs - } - } - if errs := st.PrepareReleases(helm, "template"); errs != nil && len(errs) > 0 { - return false, errs - } - releasesToRender := map[string]state.ReleaseSpec{} for _, r := range toRender { id := state.ReleaseToID(&r) diff --git a/pkg/app/run.go b/pkg/app/run.go index 113bc25c..ae01649c 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -59,7 +59,7 @@ func (r *Run) withPreparedCharts(forceDownload, skipRepos bool, helmfileCommand return err } - releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, 2, helmfileCommand, forceDownload) + releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, 2, helmfileCommand, forceDownload, skipRepos) if len(errs) > 0 { return fmt.Errorf("%v", errs) @@ -114,7 +114,6 @@ func (r *Run) Status(c StatusesConfigProvider) []error { func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) { st := r.state - helm := r.helm allReleases := st.GetReleasesWithOverrides() @@ -131,15 +130,6 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error) // on running various helm commands on unnecessary releases st.Releases = toDiff - if !c.SkipDeps() { - if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 { - return nil, false, false, errs - } - } - if errs := st.PrepareReleases(helm, "diff"); errs != nil && len(errs) > 0 { - return nil, false, false, errs - } - r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...) opts := &state.DiffOpts{ @@ -188,14 +178,6 @@ func (r *Run) Lint(c LintConfigProvider) []error { values := c.Values() args := argparser.GetArgs(c.Args(), st) workers := c.Concurrency() - if !c.SkipDeps() { - if errs := st.BuildDeps(helm); errs != nil && len(errs) > 0 { - return errs - } - } - if errs := st.PrepareReleases(helm, "lint"); errs != nil && len(errs) > 0 { - return errs - } opts := &state.LintOpts{ Set: c.Set(), } diff --git a/pkg/state/chart_dependency.go b/pkg/state/chart_dependency.go index b91e09e3..f7e7ea2e 100644 --- a/pkg/state/chart_dependency.go +++ b/pkg/state/chart_dependency.go @@ -254,7 +254,7 @@ func updateDependencies(st *HelmState, shell helmexec.DependencyUpdater, unresol _, err := depMan.Update(shell, wd, unresolved) if err != nil { - return nil, fmt.Errorf("unable to resolve %d deps: %v", len(unresolved.deps), err) + return nil, fmt.Errorf("unable to update %d deps: %v", len(unresolved.deps), err) } return resolveDependencies(st, depMan, unresolved) diff --git a/pkg/state/state.go b/pkg/state/state.go index 3984c83a..96856143 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -805,12 +805,13 @@ func releasesNeedCharts(releases []ReleaseSpec) []ReleaseSpec { // (2) temporary local chart generated from kustomization or manifests // (3) remote chart // -// When running `helmfile lint` or `helmfile template`, PrepareCharts will download and untar charts for linting and templating. +// When running `helmfile template` on helm v2, or `helmfile lint` on both helm v2 and v3, +// PrepareCharts will download and untar charts for linting and templating. // // Otheriwse, if a chart is not a helm chart, it will call "chartify" to turn it into a chart. // // If exists, it will also patch resources by json patches, strategic-merge patches, and injectors. -func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string, forceDownload bool) (map[string]string, []error) { +func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string, forceDownload, skipDeps bool) (map[string]string, []error) { releases := releasesNeedCharts(st.Releases) temp := make(map[string]string, len(releases)) @@ -831,6 +832,12 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre helm3 = helm.IsHelm3() } + updated, err := st.ResolveDeps() + if err != nil { + return nil, []error{err} + } + *st = *updated + st.scatterGather( concurrency, len(releases), @@ -842,6 +849,16 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre }, func(workerIndex int) { for release := range jobQueue { + // Call user-defined `prepare` hooks to create/modify local charts to be used by + // the later process. + // + // If it wasn't called here, Helmfile can end up an issue like + // https://github.com/roboll/helmfile/issues/1328 + if _, err := st.triggerPrepareEvent(release, helmfileCommand); err != nil { + results <- &downloadResults{err: err} + return + } + var chartPath string chartification, clean, err := st.PrepareChartify(helm, release, workerIndex) @@ -869,6 +886,13 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre chartPath = release.Chart } else if pathExists(normalizeChart(st.basePath, release.Chart)) { chartPath = normalizeChart(st.basePath, release.Chart) + + if !skipDeps { + if err := helm.BuildDeps(release.Name, chartPath); err != nil { + results <- &downloadResults{err: err} + return + } + } } else { fetchFlags := []string{} @@ -1540,35 +1564,6 @@ func (st *HelmState) FilterReleases() error { return nil } -func (st *HelmState) PrepareReleases(helm helmexec.Interface, helmfileCommand string) []error { - errs := []error{} - - for i := range st.Releases { - release := st.Releases[i] - - if release.Installed != nil && !*release.Installed { - continue - } - - if _, err := st.triggerPrepareEvent(&release, helmfileCommand); err != nil { - errs = append(errs, newReleaseFailedError(&release, err)) - continue - } - } - if len(errs) != 0 { - return errs - } - - updated, err := st.ResolveDeps() - if err != nil { - return []error{err} - } - - *st = *updated - - return nil -} - func (st *HelmState) TriggerGlobalPrepareEvent(helmfileCommand string) (bool, error) { return st.triggerGlobalReleaseEvent("prepare", nil, helmfileCommand) } @@ -1660,34 +1655,6 @@ func (st *HelmState) UpdateDeps(helm helmexec.Interface) []error { return nil } -// BuildDeps wrapper for building dependencies on the releases -func (st *HelmState) BuildDeps(helm helmexec.Interface) []error { - errs := []error{} - - releases := releasesNeedCharts(st.Releases) - - for _, release := range releases { - if len(release.Chart) == 0 { - errs = append(errs, errors.New("chart is required for: "+release.Name)) - continue - } - - if release.Installed != nil && !*release.Installed { - continue - } - - if isLocalChart(release.Chart) { - if err := helm.BuildDeps(release.Name, normalizeChart(st.basePath, release.Chart)); err != nil { - errs = append(errs, err) - } - } - } - if len(errs) != 0 { - return errs - } - return nil -} - func pathExists(chart string) bool { _, err := os.Stat(chart) return err == nil diff --git a/pkg/testhelper/testfs.go b/pkg/testhelper/testfs.go index bc99fdbf..f28d818a 100644 --- a/pkg/testhelper/testfs.go +++ b/pkg/testhelper/testfs.go @@ -70,7 +70,7 @@ func (f *TestFs) ReadFile(filename string) ([]byte, error) { str, ok = f.files[filepath.Join(f.Cwd, filename)] } if !ok { - return []byte(nil), fmt.Errorf("no registered file found: %s", filename) + return []byte(nil), os.ErrNotExist } f.fileReaderCalls += 1