diff --git a/cmd/template.go b/cmd/template.go index 52cd5bff..dfd27c38 100644 --- a/cmd/template.go +++ b/cmd/template.go @@ -47,6 +47,7 @@ func NewTemplateCmd(globalCfg *config.GlobalImpl) *cobra.Command { f.StringVar(&templateOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`) f.StringArrayVar(&templateOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`) f.StringVar(&templateOptions.KubeVersion, "kube-version", "", `pass --kube-version to "helm template". Overrides kubeVersion in helmfile.yaml`) + f.StringArrayVar(&templateOptions.ShowOnly, "show-only", nil, `pass --show-only to "helm template"`) return cmd } diff --git a/pkg/app/app.go b/pkg/app/app.go index 56bc7275..fbb35ce2 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -1964,6 +1964,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) { PostRenderer: c.PostRenderer(), PostRendererArgs: c.PostRendererArgs(), KubeVersion: c.KubeVersion(), + ShowOnly: c.ShowOnly(), } return st.TemplateReleases(helm, c.OutputDir(), c.Values(), args, c.Concurrency(), c.Validate(), opts) }) diff --git a/pkg/app/app_template_test.go b/pkg/app/app_template_test.go index 4d2548ae..cf09a932 100644 --- a/pkg/app/app_template_test.go +++ b/pkg/app/app_template_test.go @@ -24,6 +24,7 @@ func TestTemplate(t *testing.T) { skipNeeds bool includeNeeds bool includeTransitiveNeeds bool + showOnly []string } type testcase struct { @@ -131,6 +132,7 @@ releases: skipNeeds: tc.fields.skipNeeds, includeNeeds: tc.fields.includeNeeds, includeTransitiveNeeds: tc.fields.includeTransitiveNeeds, + showOnly: tc.fields.showOnly, }) var gotErr string @@ -304,6 +306,18 @@ releases: error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile", }) }) + + t.Run("show-only", func(t *testing.T) { + check(t, testcase{ + fields: fields{ + showOnly: []string{"templates/resources.yaml"}, + }, + selectors: []string{"name=logging"}, + templated: []exectest.Release{ + {Name: "logging", Flags: []string{"--show-only", "templates/resources.yaml", "--namespace", "kube-system"}}, + }, + }) + }) } func TestTemplate_StrictParsing(t *testing.T) { diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 08d8af28..ab157567 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2189,6 +2189,10 @@ func (c configImpl) KubeVersion() string { return c.kubeVersion } +func (c configImpl) ShowOnly() []string { + return nil +} + type applyConfig struct { args string cascade string @@ -2230,6 +2234,7 @@ type applyConfig struct { postRendererArgs []string kubeVersion string suppressOutputLineRegex []string + showOnly []string // template-only options includeCRDs, skipTests bool @@ -2406,6 +2411,10 @@ func (a applyConfig) KubeVersion() string { return a.kubeVersion } +func (a applyConfig) ShowOnly() []string { + return a.showOnly +} + type depsConfig struct { skipRepos bool includeTransitiveNeeds bool diff --git a/pkg/app/config.go b/pkg/app/config.go index be1c20ff..655485b7 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -233,6 +233,7 @@ type TemplateConfigProvider interface { OutputDir() string IncludeCRDs() bool KubeVersion() string + ShowOnly() []string DAGConfig diff --git a/pkg/app/testdata/app_template_test/show-only b/pkg/app/testdata/app_template_test/show-only new file mode 100644 index 00000000..16806eb9 --- /dev/null +++ b/pkg/app/testdata/app_template_test/show-only @@ -0,0 +1,113 @@ +processing file "helmfile.yaml" in directory "." +changing working directory to "/path/to" +first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode= +first-pass uses: &{default map[] map[]} +first-pass rendering output of "helmfile.yaml.part.0": + 0: + 1: releases: + 2: - name: logging + 3: chart: incubator/raw + 4: namespace: kube-system + 5: + 6: - name: kubernetes-external-secrets + 7: chart: incubator/raw + 8: namespace: kube-system + 9: needs: +10: - kube-system/logging +11: +12: - name: external-secrets +13: chart: incubator/raw +14: namespace: default +15: labels: +16: app: test +17: needs: +18: - kube-system/kubernetes-external-secrets +19: +20: - name: my-release +21: chart: incubator/raw +22: namespace: default +23: labels: +24: app: test +25: needs: +26: - default/external-secrets +27: +28: +29: # Disabled releases are treated as missing +30: - name: disabled +31: chart: incubator/raw +32: namespace: kube-system +33: installed: false +34: +35: - name: test2 +36: chart: incubator/raw +37: needs: +38: - kube-system/disabled +39: +40: - name: test3 +41: chart: incubator/raw +42: needs: +43: - test2 +44: + +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: releases: + 2: - name: logging + 3: chart: incubator/raw + 4: namespace: kube-system + 5: + 6: - name: kubernetes-external-secrets + 7: chart: incubator/raw + 8: namespace: kube-system + 9: needs: +10: - kube-system/logging +11: +12: - name: external-secrets +13: chart: incubator/raw +14: namespace: default +15: labels: +16: app: test +17: needs: +18: - kube-system/kubernetes-external-secrets +19: +20: - name: my-release +21: chart: incubator/raw +22: namespace: default +23: labels: +24: app: test +25: needs: +26: - default/external-secrets +27: +28: +29: # Disabled releases are treated as missing +30: - name: disabled +31: chart: incubator/raw +32: namespace: kube-system +33: installed: false +34: +35: - name: test2 +36: chart: incubator/raw +37: needs: +38: - kube-system/disabled +39: +40: - name: test3 +41: chart: incubator/raw +42: needs: +43: - test2 +44: + +merged environment: &{default map[] map[]} +WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs +1 release(s) matching name=logging found in helmfile.yaml + +processing 1 groups of releases in this order: +GROUP RELEASES +1 default/kube-system/logging + +processing releases in group 1/1: default/kube-system/logging +changing working directory back to "/path/to" diff --git a/pkg/config/template.go b/pkg/config/template.go index eca71c49..5ef781ea 100644 --- a/pkg/config/template.go +++ b/pkg/config/template.go @@ -38,6 +38,8 @@ type TemplateOptions struct { PostRendererArgs []string // KubeVersion is the kube-version flag KubeVersion string + // Propagate '--show-only` to helm template + ShowOnly []string } // NewTemplateOptions creates a new Apply @@ -137,3 +139,8 @@ func (t *TemplateImpl) PostRendererArgs() []string { func (t *TemplateImpl) KubeVersion() string { return t.TemplateOptions.KubeVersion } + +// ShowOnly returns the ShowOnly. +func (t *TemplateImpl) ShowOnly() []string { + return t.TemplateOptions.ShowOnly +} diff --git a/pkg/state/helmx.go b/pkg/state/helmx.go index dc7f4712..404706ac 100644 --- a/pkg/state/helmx.go +++ b/pkg/state/helmx.go @@ -119,6 +119,20 @@ func (st *HelmState) appendCascadeFlags(flags []string, helm helmexec.Interface, return flags } +// append show-only flags to helm flags +func (st *HelmState) appendShowOnlyFlags(flags []string, showOnly []string) []string { + showOnlyFlags := []string{} + if len(showOnly) != 0 { + showOnlyFlags = showOnly + } + for _, arg := range showOnlyFlags { + if arg != "" { + flags = append(flags, "--show-only", arg) + } + } + return flags +} + type Chartify struct { Opts *chartify.ChartifyOpts Clean func() diff --git a/pkg/state/helmx_test.go b/pkg/state/helmx_test.go index 74a7ba69..31f6d71c 100644 --- a/pkg/state/helmx_test.go +++ b/pkg/state/helmx_test.go @@ -249,3 +249,30 @@ func TestAppendSuppressOutputLineRegexFlags(t *testing.T) { }) } } + +func TestAppendShowOnlyFlags(t *testing.T) { + tests := []struct { + name string + templateOpts []string + expected []string + }{ + { + name: "cli template show only with 1 file", + templateOpts: []string{"templates/config.yaml"}, + expected: []string{"--show-only", "templates/config.yaml"}, + }, + { + name: "cli template show only with 2 files", + templateOpts: []string{"templates/config.yaml", "templates/resources.yaml"}, + expected: []string{"--show-only", "templates/config.yaml", "--show-only", "templates/resources.yaml"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + st := &HelmState{} + got := st.appendShowOnlyFlags([]string{}, tt.templateOpts) + require.Equalf(t, tt.expected, got, "appendShowOnlyFlags() = %v, want %v", got, tt.expected) + }) + } +} diff --git a/pkg/state/state.go b/pkg/state/state.go index 45d28017..b0b57362 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -1437,6 +1437,7 @@ type TemplateOpts struct { PostRenderer string PostRendererArgs []string KubeVersion string + ShowOnly []string } type TemplateOpt interface{ Apply(*TemplateOpts) } @@ -2691,15 +2692,18 @@ func (st *HelmState) flagsForTemplate(helm helmexec.Interface, release *ReleaseS postRenderer := "" var postRendererArgs []string kubeVersion := "" + var showOnly []string if opt != nil { postRenderer = opt.PostRenderer postRendererArgs = opt.PostRendererArgs kubeVersion = opt.KubeVersion + showOnly = opt.ShowOnly } flags = st.appendPostRenderFlags(flags, release, postRenderer) flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs) flags = st.appendApiVersionsFlags(flags, release, kubeVersion) flags = st.appendChartDownloadTLSFlags(flags, release) + flags = st.appendShowOnlyFlags(flags, showOnly) common, files, err := st.namespaceAndValuesFlags(helm, release, workerIndex) if err != nil {