fix(install): ensure --dry-run in client mode (=client, =true or empty) doesn't call kube API - fixes #30514

Signed-off-by: kilianpaquier <git@kilianpaquier.com>
pull/30944/head
kilianpaquier 4 months ago
parent 4f018e4f42
commit 621545c8fd
No known key found for this signature in database

@ -71,7 +71,6 @@ type Install struct {
ChartPathOptions ChartPathOptions
ClientOnly bool
Force bool Force bool
CreateNamespace bool CreateNamespace bool
DryRun 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 // When the task is cancelled through ctx, the function returns and the install
// proceeds in the background. // proceeds in the background.
func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) { 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`) // 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 { if err := i.cfg.KubeClient.IsReachable(); err != nil {
slog.Error(fmt.Sprintf("cluster reachability check failed: %v", err)) slog.Error(fmt.Sprintf("cluster reachability check failed: %v", err))
return nil, fmt.Errorf("cluster reachability check failed: %w", 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) 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 // Pre-install anything in the crd/ directory. We do this before Helm
// contacts the upstream server and builds the capabilities object. // 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 // On dry run, bail here
if i.isDryRun() { if i.isDryRun() {
slog.Warn("This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.") 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 // Add mock objects in here so it doesn't use Kube API server
// NOTE(bacongobbler): used for `helm template` // NOTE(bacongobbler): used for `helm template`
i.cfg.Capabilities = chartutil.DefaultCapabilities.Copy() 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 := driver.NewMemory()
mem.SetNamespace(i.Namespace) mem.SetNamespace(i.Namespace)
i.cfg.Releases = storage.Init(mem) 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") 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 // 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 // deleting the release because the manifest will be pointing at that
// resource // resource
if !i.ClientOnly && !isUpgrade && len(resources) > 0 { if interactWithRemote && !isUpgrade && len(resources) > 0 {
if i.TakeOwnership { if i.TakeOwnership {
toBeAdopted, err = requireAdoption(resources) toBeAdopted, err = requireAdoption(resources)
} else { } else {

@ -254,7 +254,7 @@ func TestInstallReleaseWithValues(t *testing.T) {
func TestInstallReleaseClientOnly(t *testing.T) { func TestInstallReleaseClientOnly(t *testing.T) {
is := assert.New(t) is := assert.New(t)
instAction := installAction(t) instAction := installAction(t)
instAction.ClientOnly = true instAction.DryRunOption = "client"
instAction.Run(buildChart(), nil) // disregard output instAction.Run(buildChart(), nil) // disregard output
is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities) is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities)

@ -89,10 +89,13 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if client.DryRunOption == "" { if client.DryRunOption == "" {
client.DryRunOption = "true" client.DryRunOption = "true"
} }
if validate {
client.DryRunOption = "server"
}
client.DryRun = true client.DryRun = true
client.ReleaseName = "release-name" client.ReleaseName = "release-name"
client.Replace = true // Skip the name check client.Replace = true // Skip the name check
client.ClientOnly = !validate
client.APIVersions = chartutil.VersionSet(extraAPIs) client.APIVersions = chartutil.VersionSet(extraAPIs)
client.IncludeCRDs = includeCrds client.IncludeCRDs = includeCrds
rel, err := runInstall(args, client, valueOpts, out) rel, err := runInstall(args, client, valueOpts, out)

Loading…
Cancel
Save