Skip to content

Commit

Permalink
Fix various issues in chart preparation (#1400)
Browse files Browse the repository at this point in the history
In #1172, we accidentally changed the meaning of prepare hook that is intended to be called BEFORE the pathExists check. It broke the scenario where one used a prepare hook for generating the local chart dynamically. This fixes Helmfile not to fetch local chart generated by prepare hook.

In addition to that, this patch results in the following fixes:

- Fix an issue that `helmfile template` without `--skip-deps` fails while trying to run `helm dep build` on `helm fetch`ed chart, when the remote chart has outdated dependencies in the Chart.lock file. It should be up to the chart maintainer to update Chart.lock and the user should not be blocked due to that. So, after this patch `helm dep build` is run only on the local chart, not on fetched remote chart.
- Skip fetching chart on `helmfile template` when using Helm v3. `helm template` in helm v3 does support rendering remote charts so we do not need to fetch beforehand.

Fixes #1328
May relate to #1341
  • Loading branch information
mumoshu authored Aug 6, 2020
1 parent 53c3fe9 commit b85243a
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 108 deletions.
32 changes: 4 additions & 28 deletions pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand All @@ -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)
})
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 1 addition & 19 deletions pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()

Expand All @@ -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{
Expand Down Expand Up @@ -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(),
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/state/chart_dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
85 changes: 26 additions & 59 deletions pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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),
Expand All @@ -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)
Expand Down Expand Up @@ -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{}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/testhelper/testfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b85243a

Please sign in to comment.