From 228aab70a95d26ddd02529857557dd40d946fbf7 Mon Sep 17 00:00:00 2001 From: Kerstin Albers Date: Fri, 21 Feb 2025 13:47:46 +0100 Subject: [PATCH] test: add test for prepare charts with template args Signed-off-by: Kerstin Albers --- pkg/app/run.go | 19 +++++++- pkg/state/state.go | 23 +++++---- pkg/state/state_test.go | 103 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 11 deletions(-) diff --git a/pkg/app/run.go b/pkg/app/run.go index 2beab11a..811cbb6a 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/fatih/color" + "github.com/helmfile/chartify" "github.com/helmfile/helmfile/pkg/helmexec" "github.com/helmfile/helmfile/pkg/state" @@ -42,6 +43,14 @@ func (r *Run) askForConfirmation(msg string) bool { return AskForConfirmation(msg) } +type Chartifier struct { + runner *chartify.Runner +} + +func (c *Chartifier) Chartify(release, dirOrChart string, opts ...chartify.ChartifyOption) (string, error) { + return c.runner.Chartify(release, dirOrChart, opts...) +} + func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurrency int, opts state.ChartPrepareOptions) (map[state.PrepareChartKey]string, error) { // Skip chart preparation for certain commands skipCommands := []string{"write-values", "list"} @@ -49,7 +58,15 @@ func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurre return nil, nil } - releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, concurrency, helmfileCommand, opts) + chartifier := &Chartifier{ + runner: chartify.New( + chartify.HelmBin(r.state.DefaultHelmBinary), + chartify.KustomizeBin(r.state.DefaultKustomizeBinary), + chartify.UseHelm3(true), + chartify.WithLogf(r.state.Logger().Debugf)), + } + + releaseToChart, errs := r.state.PrepareCharts(r.helm, chartifier, dir, concurrency, helmfileCommand, opts) if len(errs) > 0 { return nil, fmt.Errorf("%v", errs) } diff --git a/pkg/state/state.go b/pkg/state/state.go index 54b0b9f7..2a31dca1 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1209,6 +1209,16 @@ type PrepareChartKey struct { Namespace, Name, KubeContext string } +func (st *HelmState) Logger() *zap.SugaredLogger { + return st.logger +} + +type Chartifier interface { + // Chartify creates a temporary Helm chart from a directory or a remote chart, and applies various transformations. + // Returns the full path to the temporary directory containing the generated chart if succeeded. + Chartify(release, dirOrChart string, opts ...chartify.ChartifyOption) (string, error) +} + // PrepareCharts creates temporary directories of charts. // // Each resulting "chart" can be one of the followings: @@ -1220,10 +1230,10 @@ type PrepareChartKey struct { // 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. +// Otherwise, 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, opts ChartPrepareOptions) (map[PrepareChartKey]string, []error) { +func (st *HelmState) PrepareCharts(helm helmexec.Interface, chartifier Chartifier, dir string, concurrency int, helmfileCommand string, opts ChartPrepareOptions) (map[PrepareChartKey]string, []error) { if !opts.SkipResolve { updated, err := st.ResolveDeps() if err != nil { @@ -1317,13 +1327,6 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre skipDeps := (!isLocal && !chartFetchedByGoGetter) || skipDepsGlobal || skipDepsRelease || skipDepsDefault if chartification != nil && helmfileCommand != "pull" { - c := chartify.New( - chartify.HelmBin(st.DefaultHelmBinary), - chartify.KustomizeBin(st.DefaultKustomizeBinary), - chartify.UseHelm3(true), - chartify.WithLogf(st.logger.Debugf), - ) - chartifyOpts := chartification.Opts if skipDeps { @@ -1357,7 +1360,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre } chartifyOpts.SetFlags = append(chartifyOpts.SetFlags, flags...) - out, err := c.Chartify(release.Name, chartPath, chartify.WithChartifyOpts(chartifyOpts)) + out, err := chartifier.Chartify(release.Name, chartPath, chartify.WithChartifyOpts(chartifyOpts)) if err != nil { results <- &chartPrepareResult{err: err} return diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 57774b23..70be5035 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -4,11 +4,13 @@ import ( "fmt" "io" "os" + "path" "path/filepath" "reflect" "testing" "github.com/Masterminds/semver/v3" + "github.com/helmfile/chartify" "github.com/helmfile/vals" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -2974,6 +2976,107 @@ func TestDiffpareSyncReleases(t *testing.T) { } } +// capturingChartifier implements state.Chartifier for tests. +type capturingChartifier struct { + mChartify func(release string, dirOrChart string, opts ...chartify.ChartifyOption) (string, error) + capturedArgs []chartifyArgs +} + +type chartifyArgs struct { + release string + dirOrChart string + opts []chartify.ChartifyOption +} + +func (c *capturingChartifier) Chartify(release string, dirOrChart string, opts ...chartify.ChartifyOption) (string, error) { + c.capturedArgs = append(c.capturedArgs, chartifyArgs{release: release, dirOrChart: dirOrChart, opts: opts}) + return c.mChartify(release, dirOrChart, opts...) +} + +func TestPrepareCharts_invokesChartifierWithTemplateArgs(t *testing.T) { + tests := []struct { + name string + chartifier *capturingChartifier + dir string + concurrency int + command string + opts ChartPrepareOptions + expectedArgs []chartifyArgs + helmDefaults *HelmSpec + }{ + { + name: "template command", + chartifier: &capturingChartifier{ + mChartify: func(release string, dirOrChart string, opts ...chartify.ChartifyOption) (string, error) { + return "someTempDir", nil + }, + }, + dir: "someChartPath", + concurrency: 1, + command: "template", + opts: ChartPrepareOptions{ + SkipDeps: true, + TemplateArgs: "someArgs", + }, + helmDefaults: &HelmSpec{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "test-prepare-charts-*") + if err != nil { + t.Fatalf("setup temp test dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + err = os.Mkdir(path.Join(tmpDir, tt.dir), 0755) + if err != nil { + t.Fatalf("setup chart dir: %v", err) + } + + release := ReleaseSpec{ + Name: tt.name, + } + releases := []ReleaseSpec{ + release, + } + + state := &HelmState{ + basePath: tmpDir, + ReleaseSetSpec: ReleaseSetSpec{ + Releases: releases, + HelmDefaults: *tt.helmDefaults, + }, + logger: logger, + valsRuntime: valsRuntime, + fs: filesystem.DefaultFileSystem(), + RenderedValues: map[string]any{}, + } + helm := &exectest.Helm{ + Lists: map[exectest.ListKey]string{}, + Helm3: true, + } + + _, errs := state.PrepareCharts(helm, tt.chartifier, path.Join(tmpDir, tt.dir), tt.concurrency, tt.command, tt.opts) + + require.Len(t, errs, 0) + require.Len(t, tt.chartifier.capturedArgs, 1) + + appliedChartifyOpts := &chartify.ChartifyOpts{} + + for _, o := range tt.chartifier.capturedArgs[0].opts { + err = o.SetChartifyOption(appliedChartifyOpts) + if err != nil { + t.Fatalf("set captured chartify options: %v", err) + } + } + + require.Equal(t, tt.opts.TemplateArgs, appliedChartifyOpts.TemplateArgs) + }) + } +} + func TestPrepareSyncReleases(t *testing.T) { tests := []struct { name string