diff --git a/api/internal/builtins/HelmChartInflationGenerator.go b/api/internal/builtins/HelmChartInflationGenerator.go index c93c18c009..06d13b5261 100644 --- a/api/internal/builtins/HelmChartInflationGenerator.go +++ b/api/internal/builtins/HelmChartInflationGenerator.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "regexp" + "slices" "strings" "sigs.k8s.io/kustomize/api/resmap" @@ -62,6 +63,9 @@ func (p *HelmChartInflationGeneratorPlugin) Config( if len(h.GeneralConfig().HelmConfig.ApiVersions) != 0 { p.HelmChart.ApiVersions = h.GeneralConfig().HelmConfig.ApiVersions } + if h.GeneralConfig().HelmConfig.Debug { + p.HelmChart.Debug = h.GeneralConfig().HelmConfig.Debug + } p.h = h if err = yaml.Unmarshal(config, p); err != nil { @@ -168,13 +172,17 @@ func (p *HelmChartInflationGeneratorPlugin) runHelmCommand( fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)} cmd.Env = append(os.Environ(), env...) err := cmd.Run() + errorOutput := stderr.String() + if slices.Contains(args, "--debug") { + errorOutput = " Helm stack trace:\n" + errorOutput + "\nHelm template:\n" + stdout.String() + "\n" + } if err != nil { helm := p.h.GeneralConfig().HelmConfig.Command err = errors.WrapPrefixf( fmt.Errorf( "unable to run: '%s %s' with env=%s (is '%s' installed?): %w", helm, strings.Join(args, " "), env, helm, err), - stderr.String(), + errorOutput, ) } return stdout.Bytes(), err diff --git a/api/types/helmchartargs.go b/api/types/helmchartargs.go index 1ed89c01ce..b96fbfb166 100644 --- a/api/types/helmchartargs.go +++ b/api/types/helmchartargs.go @@ -96,6 +96,9 @@ type HelmChart struct { // SkipTests skips tests from templated output. SkipTests bool `json:"skipTests,omitempty" yaml:"skipTests,omitempty"` + + // debug enables debug output from the Helm chart inflator generator. + Debug bool `json:"debug,omitempty" yaml:"debug,omitempty"` } // HelmChartArgs contains arguments to helm. @@ -188,5 +191,8 @@ func (h HelmChart) AsHelmArgs(absChartHome string) []string { if h.SkipHooks { args = append(args, "--no-hooks") } + if h.Debug { + args = append(args, "--debug") + } return args } diff --git a/api/types/helmchartargs_test.go b/api/types/helmchartargs_test.go index 9827360d9b..935c5794b4 100644 --- a/api/types/helmchartargs_test.go +++ b/api/types/helmchartargs_test.go @@ -60,4 +60,21 @@ func TestAsHelmArgs(t *testing.T) { "-f", "values1", "-f", "values2", "--api-versions", "foo", "--api-versions", "bar"}) }) + + t.Run("use helm-debug", func(t *testing.T) { + p := types.HelmChart{ + Name: "chart-name", + Version: "1.0.0", + Repo: "https://helm.releases.hashicorp.com", + ValuesFile: "values", + AdditionalValuesFiles: []string{"values1", "values2"}, + Debug: true, + } + require.Equal(t, p.AsHelmArgs("/home/charts"), + []string{"template", "--generate-name", "/home/charts/chart-name", + "-f", "values", + "-f", "values1", + "-f", "values2", + "--debug"}) + }) } diff --git a/api/types/pluginconfig.go b/api/types/pluginconfig.go index aa511ae79c..36f0abf428 100644 --- a/api/types/pluginconfig.go +++ b/api/types/pluginconfig.go @@ -8,6 +8,7 @@ type HelmConfig struct { Command string ApiVersions []string KubeVersion string + Debug bool } // PluginConfig holds plugin configuration. diff --git a/kustomize/commands/build/build.go b/kustomize/commands/build/build.go index 96dcc72605..21ecaeb6ff 100644 --- a/kustomize/commands/build/build.go +++ b/kustomize/commands/build/build.go @@ -30,6 +30,7 @@ var theFlags struct { helmCommand string helmApiVersions []string helmKubeVersion string + helmDebug bool loadRestrictor string reorderOutput string fnOptions types.FnPluginLoadingOptions @@ -163,6 +164,7 @@ func HonorKustomizeFlags(kOpts *krusty.Options, flags *flag.FlagSet) *krusty.Opt kOpts.PluginConfig.HelmConfig.Command = theFlags.helmCommand kOpts.PluginConfig.HelmConfig.ApiVersions = theFlags.helmApiVersions kOpts.PluginConfig.HelmConfig.KubeVersion = theFlags.helmKubeVersion + kOpts.PluginConfig.HelmConfig.Debug = theFlags.helmDebug kOpts.AddManagedbyLabel = isManagedByLabelEnabled() return kOpts } diff --git a/kustomize/commands/build/flagenablehelm.go b/kustomize/commands/build/flagenablehelm.go index a379d180a5..d7d02d6029 100644 --- a/kustomize/commands/build/flagenablehelm.go +++ b/kustomize/commands/build/flagenablehelm.go @@ -31,4 +31,9 @@ func AddFlagEnableHelm(set *pflag.FlagSet) { "helm-kube-version", "", // default "Kubernetes version used by Helm for Capabilities.KubeVersion") + set.BoolVar( + &theFlags.helmDebug, + "helm-debug", + false, + "Enable debug output from the Helm chart inflator generator.") } diff --git a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go index 1f7f28662f..88527fe074 100644 --- a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go +++ b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go @@ -14,6 +14,7 @@ import ( "os/exec" "path/filepath" "regexp" + "slices" "strings" "sigs.k8s.io/kustomize/api/resmap" @@ -68,6 +69,9 @@ func (p *plugin) Config( if len(h.GeneralConfig().HelmConfig.ApiVersions) != 0 { p.HelmChart.ApiVersions = h.GeneralConfig().HelmConfig.ApiVersions } + if h.GeneralConfig().HelmConfig.Debug { + p.HelmChart.Debug = h.GeneralConfig().HelmConfig.Debug + } p.h = h if err = yaml.Unmarshal(config, p); err != nil { @@ -174,13 +178,17 @@ func (p *plugin) runHelmCommand( fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)} cmd.Env = append(os.Environ(), env...) err := cmd.Run() + errorOutput := stderr.String() + if slices.Contains(args, "--debug") { + errorOutput = " Helm stack trace:\n" + errorOutput + "\nHelm template:\n" + stdout.String() + "\n" + } if err != nil { helm := p.h.GeneralConfig().HelmConfig.Command err = errors.WrapPrefixf( fmt.Errorf( "unable to run: '%s %s' with env=%s (is '%s' installed?): %w", helm, strings.Join(args, " "), env, helm, err), - stderr.String(), + errorOutput, ) } return stdout.Bytes(), err diff --git a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator_test.go b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator_test.go index 9fac50752a..e6b414e9ec 100644 --- a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator_test.go +++ b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator_test.go @@ -959,3 +959,37 @@ chartHome: ./charts assert.Contains(t, string(chartYamlContent), "name: test-chart") assert.Contains(t, string(chartYamlContent), "version: 1.0.0") } + +func TestHelmChartInflationGeneratorWithDebug(t *testing.T) { + th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t). + PrepBuiltin("HelmChartInflationGenerator") + defer th.Reset() + if err := th.ErrIfNoHelm(); err != nil { + t.Skip("skipping: " + err.Error()) + } + copyTestChartsIntoHarness(t, th) + + rm := th.LoadAndRunGenerator(` +apiVersion: builtin +kind: HelmChartInflationGenerator +metadata: + name: test-chart +name: test-chart +version: 1.0.0 +releaseName: test +chartHome: ./charts +debug: true +`) + + cm, err := rm.Resources()[0].GetFieldValue("metadata.name") + require.NoError(t, err) + assert.Equal(t, "bar", cm) + + chartDir := filepath.Join(th.GetRoot(), "charts/test-chart") + assert.True(t, th.GetFSys().Exists(chartDir)) + + chartYamlContent, err := th.GetFSys().ReadFile(filepath.Join(chartDir, "Chart.yaml")) + require.NoError(t, err) + assert.Contains(t, string(chartYamlContent), "name: test-chart") + assert.Contains(t, string(chartYamlContent), "version: 1.0.0") +}