From 621545c8fd77ab9c59e79e97514b0734dc5cc300 Mon Sep 17 00:00:00 2001 From: kilianpaquier Date: Sat, 31 May 2025 14:43:00 +0000 Subject: [PATCH] fix(install): ensure --dry-run in client mode (=client, =true or empty) doesn't call kube API - fixes #30514 Signed-off-by: kilianpaquier --- pkg/action/install.go | 21 ++++++++++----------- pkg/action/install_test.go | 2 +- pkg/cmd/template.go | 5 ++++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 440f41baa..cf7012602 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -71,7 +71,6 @@ type Install struct { ChartPathOptions - ClientOnly bool Force bool CreateNamespace bool DryRun bool @@ -236,8 +235,13 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. // When the task is cancelled through ctx, the function returns and the install // proceeds in the background. func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) { + var interactWithRemote bool + if !i.isDryRun() || i.DryRunOption == "server" || i.DryRunOption == "none" || i.DryRunOption == "false" { + interactWithRemote = true + } + // Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`) - if !i.ClientOnly { + if interactWithRemote { if err := i.cfg.KubeClient.IsReachable(); err != nil { slog.Error(fmt.Sprintf("cluster reachability check failed: %v", err)) return nil, fmt.Errorf("cluster reachability check failed: %w", err) @@ -260,14 +264,9 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma return nil, fmt.Errorf("chart dependencies processing failed: %w", err) } - var interactWithRemote bool - if !i.isDryRun() || i.DryRunOption == "server" || i.DryRunOption == "none" || i.DryRunOption == "false" { - interactWithRemote = true - } - // Pre-install anything in the crd/ directory. We do this before Helm // contacts the upstream server and builds the capabilities object. - if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 { + if crds := chrt.CRDObjects(); interactWithRemote && !i.SkipCRDs && len(crds) > 0 { // On dry run, bail here if i.isDryRun() { slog.Warn("This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.") @@ -276,7 +275,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma } } - if i.ClientOnly { + if !interactWithRemote { // Add mock objects in here so it doesn't use Kube API server // NOTE(bacongobbler): used for `helm template` i.cfg.Capabilities = chartutil.DefaultCapabilities.Copy() @@ -289,7 +288,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma mem := driver.NewMemory() mem.SetNamespace(i.Namespace) i.cfg.Releases = storage.Init(mem) - } else if !i.ClientOnly && len(i.APIVersions) > 0 { + } else if interactWithRemote && len(i.APIVersions) > 0 { slog.Debug("API Version list given outside of client only mode, this list will be ignored") } @@ -358,7 +357,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma // we'll end up in a state where we will delete those resources upon // deleting the release because the manifest will be pointing at that // resource - if !i.ClientOnly && !isUpgrade && len(resources) > 0 { + if interactWithRemote && !isUpgrade && len(resources) > 0 { if i.TakeOwnership { toBeAdopted, err = requireAdoption(resources) } else { diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 6c2c91d0a..e254ac0c2 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -254,7 +254,7 @@ func TestInstallReleaseWithValues(t *testing.T) { func TestInstallReleaseClientOnly(t *testing.T) { is := assert.New(t) instAction := installAction(t) - instAction.ClientOnly = true + instAction.DryRunOption = "client" instAction.Run(buildChart(), nil) // disregard output is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities) diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index 7a565ef85..1f8ab9261 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -89,10 +89,13 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if client.DryRunOption == "" { client.DryRunOption = "true" } + if validate { + client.DryRunOption = "server" + } + client.DryRun = true client.ReleaseName = "release-name" client.Replace = true // Skip the name check - client.ClientOnly = !validate client.APIVersions = chartutil.VersionSet(extraAPIs) client.IncludeCRDs = includeCrds rel, err := runInstall(args, client, valueOpts, out)