mirror of
https://github.com/helm/helm.git
synced 2026-08-08 17:11:18 +00:00
Merge pull request #30833 from gjenkins8/gjenkins/refactor_dry_run_flag
refactor/cleanup: Replace action 'DryRun' string with DryRunStrategy type + deprecations
This commit is contained in:
@@ -70,6 +70,21 @@ var (
|
||||
errPending = errors.New("another operation (install/upgrade/rollback) is in progress")
|
||||
)
|
||||
|
||||
type DryRunStrategy string
|
||||
|
||||
const (
|
||||
// DryRunNone indicates the client will make all mutating calls
|
||||
DryRunNone DryRunStrategy = "none"
|
||||
|
||||
// DryRunClient, or client-side dry-run, indicates the client will avoid
|
||||
// making calls to the server
|
||||
DryRunClient DryRunStrategy = "client"
|
||||
|
||||
// DryRunServer, or server-side dry-run, indicates the client will send
|
||||
// calls to the APIServer with the dry-run parameter to prevent persisting changes
|
||||
DryRunServer DryRunStrategy = "server"
|
||||
)
|
||||
|
||||
// Configuration injects the dependencies that all actions share.
|
||||
type Configuration struct {
|
||||
// RESTClientGetter is an interface that loads Kubernetes clients.
|
||||
@@ -528,3 +543,13 @@ func determineReleaseSSApplyMethod(serverSideApply bool) release.ApplyMethod {
|
||||
}
|
||||
return release.ApplyMethodClientSideApply
|
||||
}
|
||||
|
||||
// isDryRun returns true if the strategy is set to run as a DryRun
|
||||
func isDryRun(strategy DryRunStrategy) bool {
|
||||
return strategy == DryRunClient || strategy == DryRunServer
|
||||
}
|
||||
|
||||
// interactWithServer determine whether or not to interact with a remote Kubernetes server
|
||||
func interactWithServer(strategy DryRunStrategy) bool {
|
||||
return strategy == DryRunNone || strategy == DryRunServer
|
||||
}
|
||||
|
||||
@@ -951,3 +951,15 @@ func TestDetermineReleaseSSAApplyMethod(t *testing.T) {
|
||||
assert.Equal(t, release.ApplyMethodClientSideApply, determineReleaseSSApplyMethod(false))
|
||||
assert.Equal(t, release.ApplyMethodServerSideApply, determineReleaseSSApplyMethod(true))
|
||||
}
|
||||
|
||||
func TestIsDryRun(t *testing.T) {
|
||||
assert.False(t, isDryRun(DryRunNone))
|
||||
assert.True(t, isDryRun(DryRunClient))
|
||||
assert.True(t, isDryRun(DryRunServer))
|
||||
}
|
||||
|
||||
func TestInteractWithServer(t *testing.T) {
|
||||
assert.True(t, interactWithServer(DryRunNone))
|
||||
assert.False(t, interactWithServer(DryRunClient))
|
||||
assert.True(t, interactWithServer(DryRunServer))
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ type Install struct {
|
||||
|
||||
ChartPathOptions
|
||||
|
||||
ClientOnly bool
|
||||
// ForceReplace will, if set to `true`, ignore certain warnings and perform the install anyway.
|
||||
//
|
||||
// This should be used with caution.
|
||||
@@ -86,8 +85,8 @@ type Install struct {
|
||||
// see: https://kubernetes.io/docs/reference/using-api/server-side-apply/
|
||||
ServerSideApply bool
|
||||
CreateNamespace bool
|
||||
DryRun bool
|
||||
DryRunOption string
|
||||
// DryRunStrategy can be set to prepare, but not execute the operation and whether or not to interact with the remote cluster
|
||||
DryRunStrategy DryRunStrategy
|
||||
// HideSecret can be set to true when DryRun is enabled in order to hide
|
||||
// Kubernetes Secrets in the output. It cannot be used outside of DryRun.
|
||||
HideSecret bool
|
||||
@@ -158,6 +157,7 @@ func NewInstall(cfg *Configuration) *Install {
|
||||
in := &Install{
|
||||
cfg: cfg,
|
||||
ServerSideApply: true,
|
||||
DryRunStrategy: DryRunNone,
|
||||
}
|
||||
in.registryClient = cfg.RegistryClient
|
||||
|
||||
@@ -263,8 +263,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
return nil, errors.New("invalid chart apiVersion")
|
||||
}
|
||||
|
||||
// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
|
||||
if !i.ClientOnly {
|
||||
if interactWithServer(i.DryRunStrategy) {
|
||||
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)
|
||||
@@ -272,7 +271,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
}
|
||||
|
||||
// HideSecret must be used with dry run. Otherwise, return an error.
|
||||
if !i.isDryRun() && i.HideSecret {
|
||||
if !isDryRun(i.DryRunStrategy) && i.HideSecret {
|
||||
slog.Error("hiding Kubernetes secrets requires a dry-run mode")
|
||||
return nil, errors.New("hiding Kubernetes secrets requires a dry-run mode")
|
||||
}
|
||||
@@ -287,23 +286,18 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
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(); interactWithServer(i.DryRunStrategy) && !i.SkipCRDs && len(crds) > 0 {
|
||||
// On dry run, bail here
|
||||
if i.isDryRun() {
|
||||
if isDryRun(i.DryRunStrategy) {
|
||||
slog.Warn("This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
|
||||
} else if err := i.installCRDs(crds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if i.ClientOnly {
|
||||
if !interactWithServer(i.DryRunStrategy) {
|
||||
// Add mock objects in here so it doesn't use Kube API server
|
||||
// NOTE(bacongobbler): used for `helm template`
|
||||
i.cfg.Capabilities = common.DefaultCapabilities.Copy()
|
||||
@@ -316,7 +310,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
mem := driver.NewMemory()
|
||||
mem.SetNamespace(i.Namespace)
|
||||
i.cfg.Releases = storage.Init(mem)
|
||||
} else if !i.ClientOnly && len(i.APIVersions) > 0 {
|
||||
} else if interactWithServer(i.DryRunStrategy) && len(i.APIVersions) > 0 {
|
||||
slog.Debug("API Version list given outside of client only mode, this list will be ignored")
|
||||
}
|
||||
|
||||
@@ -332,7 +326,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
}
|
||||
|
||||
// special case for helm template --is-upgrade
|
||||
isUpgrade := i.IsUpgrade && i.isDryRun()
|
||||
isUpgrade := i.IsUpgrade && isDryRun(i.DryRunStrategy)
|
||||
options := common.ReleaseOptions{
|
||||
Name: i.ReleaseName,
|
||||
Namespace: i.Namespace,
|
||||
@@ -352,7 +346,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
rel := i.createRelease(chrt, vals, i.Labels)
|
||||
|
||||
var manifestDoc *bytes.Buffer
|
||||
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, interactWithRemote, i.EnableDNS, i.HideSecret)
|
||||
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, interactWithServer(i.DryRunStrategy), i.EnableDNS, i.HideSecret)
|
||||
// Even for errors, attach this if available
|
||||
if manifestDoc != nil {
|
||||
rel.Manifest = manifestDoc.String()
|
||||
@@ -385,7 +379,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
// 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 interactWithServer(i.DryRunStrategy) && !isUpgrade && len(resources) > 0 {
|
||||
if i.TakeOwnership {
|
||||
toBeAdopted, err = requireAdoption(resources)
|
||||
} else {
|
||||
@@ -397,7 +391,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
|
||||
}
|
||||
|
||||
// Bail out here if it is a dry run
|
||||
if i.isDryRun() {
|
||||
if isDryRun(i.DryRunStrategy) {
|
||||
rel.Info.Description = "Dry run complete"
|
||||
return rel, nil
|
||||
}
|
||||
@@ -479,14 +473,6 @@ func (i *Install) getGoroutineCount() int32 {
|
||||
return i.goroutineCount.Load()
|
||||
}
|
||||
|
||||
// isDryRun returns true if Upgrade is set to run as a DryRun
|
||||
func (i *Install) isDryRun() bool {
|
||||
if i.DryRun || i.DryRunOption == "client" || i.DryRunOption == "server" || i.DryRunOption == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.ResourceList, resources kube.ResourceList) (*release.Release, error) {
|
||||
var err error
|
||||
// pre-install hooks
|
||||
@@ -589,7 +575,7 @@ func (i *Install) availableName() error {
|
||||
return fmt.Errorf("release name %q: %w", start, err)
|
||||
}
|
||||
// On dry run, bail here
|
||||
if i.isDryRun() {
|
||||
if isDryRun(i.DryRunStrategy) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -249,16 +249,6 @@ func TestInstallReleaseWithValues(t *testing.T) {
|
||||
is.Equal(expectedUserValues, rel.Config)
|
||||
}
|
||||
|
||||
func TestInstallReleaseClientOnly(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.ClientOnly = true
|
||||
instAction.Run(buildChart(), nil) // disregard output
|
||||
|
||||
is.Equal(instAction.cfg.Capabilities, common.DefaultCapabilities)
|
||||
is.Equal(instAction.cfg.KubeClient, &kubefake.PrintingKubeClient{Out: io.Discard})
|
||||
}
|
||||
|
||||
func TestInstallRelease_NoName(t *testing.T) {
|
||||
instAction := installAction(t)
|
||||
instAction.ReleaseName = ""
|
||||
@@ -355,27 +345,30 @@ func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
|
||||
is.Equal(rel.Info.Description, "Install complete")
|
||||
}
|
||||
|
||||
func TestInstallRelease_DryRun(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRun = true
|
||||
vals := map[string]interface{}{}
|
||||
res, err := instAction.Run(buildChart(withSampleTemplates()), vals)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed install: %s", err)
|
||||
func TestInstallRelease_DryRunClient(t *testing.T) {
|
||||
for _, dryRunStrategy := range []DryRunStrategy{DryRunClient, DryRunServer} {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRunStrategy = dryRunStrategy
|
||||
|
||||
vals := map[string]interface{}{}
|
||||
res, err := instAction.Run(buildChart(withSampleTemplates()), vals)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed install: %s", err)
|
||||
}
|
||||
|
||||
is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
|
||||
is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world")
|
||||
is.Contains(res.Manifest, "hello: Earth")
|
||||
is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}")
|
||||
is.NotContains(res.Manifest, "empty")
|
||||
|
||||
_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
|
||||
is.Error(err)
|
||||
is.Len(res.Hooks, 1)
|
||||
is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run")
|
||||
is.Equal(res.Info.Description, "Dry run complete")
|
||||
}
|
||||
|
||||
is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
|
||||
is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world")
|
||||
is.Contains(res.Manifest, "hello: Earth")
|
||||
is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}")
|
||||
is.NotContains(res.Manifest, "empty")
|
||||
|
||||
_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
|
||||
is.Error(err)
|
||||
is.Len(res.Hooks, 1)
|
||||
is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run")
|
||||
is.Equal(res.Info.Description, "Dry run complete")
|
||||
}
|
||||
|
||||
func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
|
||||
@@ -383,7 +376,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
|
||||
instAction := installAction(t)
|
||||
|
||||
// First perform a normal dry-run with the secret and confirm its presence.
|
||||
instAction.DryRun = true
|
||||
instAction.DryRunStrategy = DryRunClient
|
||||
vals := map[string]interface{}{}
|
||||
res, err := instAction.Run(buildChart(withSampleSecret(), withSampleTemplates()), vals)
|
||||
if err != nil {
|
||||
@@ -410,7 +403,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
|
||||
is.Equal(res2.Info.Description, "Dry run complete")
|
||||
|
||||
// Ensure there is an error when HideSecret True but not in a dry-run mode
|
||||
instAction.DryRun = false
|
||||
instAction.DryRunStrategy = DryRunNone
|
||||
vals = map[string]interface{}{}
|
||||
_, err = instAction.Run(buildChart(withSampleSecret(), withSampleTemplates()), vals)
|
||||
if err == nil {
|
||||
@@ -422,7 +415,7 @@ func TestInstallRelease_DryRunHiddenSecret(t *testing.T) {
|
||||
func TestInstallRelease_DryRun_Lookup(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRun = true
|
||||
instAction.DryRunStrategy = DryRunNone
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
mockChart := buildChart(withSampleTemplates())
|
||||
@@ -442,7 +435,7 @@ func TestInstallRelease_DryRun_Lookup(t *testing.T) {
|
||||
func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRun = true
|
||||
instAction.DryRunStrategy = DryRunNone
|
||||
vals := map[string]interface{}{}
|
||||
_, err := instAction.Run(buildChart(withSampleIncludingIncorrectTemplates()), vals)
|
||||
expectedErr := `hello/templates/incorrect:1:10
|
||||
@@ -1001,7 +994,6 @@ func TestInstallRun_UnreachableKubeClient(t *testing.T) {
|
||||
config.KubeClient = &failingKubeClient
|
||||
|
||||
instAction := NewInstall(config)
|
||||
instAction.ClientOnly = false
|
||||
ctx, done := context.WithCancel(t.Context())
|
||||
chrt := buildChart()
|
||||
res, err := instAction.RunWithContext(ctx, chrt, nil)
|
||||
|
||||
@@ -39,7 +39,8 @@ type Rollback struct {
|
||||
WaitStrategy kube.WaitStrategy
|
||||
WaitForJobs bool
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
// DryRunStrategy can be set to prepare, but not execute the operation and whether or not to interact with the remote cluster
|
||||
DryRunStrategy DryRunStrategy
|
||||
// ForceReplace will, if set to `true`, ignore certain warnings and perform the rollback anyway.
|
||||
//
|
||||
// This should be used with caution.
|
||||
@@ -59,7 +60,8 @@ type Rollback struct {
|
||||
// NewRollback creates a new Rollback object with the given configuration.
|
||||
func NewRollback(cfg *Configuration) *Rollback {
|
||||
return &Rollback{
|
||||
cfg: cfg,
|
||||
cfg: cfg,
|
||||
DryRunStrategy: DryRunNone,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +79,7 @@ func (r *Rollback) Run(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
if !isDryRun(r.DryRunStrategy) {
|
||||
slog.Debug("creating rolled back release", "name", name)
|
||||
if err := r.cfg.Releases.Create(targetRelease); err != nil {
|
||||
return err
|
||||
@@ -89,7 +91,7 @@ func (r *Rollback) Run(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
if !isDryRun(r.DryRunStrategy) {
|
||||
slog.Debug("updating status for rolled back release", "name", name)
|
||||
if err := r.cfg.Releases.Update(targetRelease); err != nil {
|
||||
return err
|
||||
@@ -175,7 +177,7 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
|
||||
}
|
||||
|
||||
func (r *Rollback) performRollback(currentRelease, targetRelease *release.Release, serverSideApply bool) (*release.Release, error) {
|
||||
if r.DryRun {
|
||||
if isDryRun(r.DryRunStrategy) {
|
||||
slog.Debug("dry run", "name", targetRelease.Name)
|
||||
return targetRelease, nil
|
||||
}
|
||||
|
||||
@@ -73,10 +73,8 @@ type Upgrade struct {
|
||||
WaitForJobs bool
|
||||
// DisableHooks disables hook processing if set to true.
|
||||
DisableHooks bool
|
||||
// DryRun controls whether the operation is prepared, but not executed.
|
||||
DryRun bool
|
||||
// DryRunOption controls whether the operation is prepared, but not executed with options on whether or not to interact with the remote cluster.
|
||||
DryRunOption string
|
||||
// DryRunStrategy can be set to prepare, but not execute the operation and whether or not to interact with the remote cluster
|
||||
DryRunStrategy DryRunStrategy
|
||||
// HideSecret can be set to true when DryRun is enabled in order to hide
|
||||
// Kubernetes Secrets in the output. It cannot be used outside of DryRun.
|
||||
HideSecret bool
|
||||
@@ -140,6 +138,7 @@ func NewUpgrade(cfg *Configuration) *Upgrade {
|
||||
up := &Upgrade{
|
||||
cfg: cfg,
|
||||
ServerSideApply: "auto",
|
||||
DryRunStrategy: DryRunNone,
|
||||
}
|
||||
up.registryClient = cfg.RegistryClient
|
||||
|
||||
@@ -198,7 +197,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, ch chart.Char
|
||||
}
|
||||
|
||||
// Do not update for dry runs
|
||||
if !u.isDryRun() {
|
||||
if !isDryRun(u.DryRunStrategy) {
|
||||
slog.Debug("updating status for upgraded release", "name", name)
|
||||
if err := u.cfg.Releases.Update(upgradedRelease); err != nil {
|
||||
return res, err
|
||||
@@ -208,14 +207,6 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, ch chart.Char
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// isDryRun returns true if Upgrade is set to run as a DryRun
|
||||
func (u *Upgrade) isDryRun() bool {
|
||||
if u.DryRun || u.DryRunOption == "client" || u.DryRunOption == "server" || u.DryRunOption == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// prepareUpgrade builds an upgraded release for an upgrade operation.
|
||||
func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[string]interface{}) (*release.Release, *release.Release, bool, error) {
|
||||
if chart == nil {
|
||||
@@ -223,7 +214,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
|
||||
}
|
||||
|
||||
// HideSecret must be used with dry run. Otherwise, return an error.
|
||||
if !u.isDryRun() && u.HideSecret {
|
||||
if !isDryRun(u.DryRunStrategy) && u.HideSecret {
|
||||
return nil, nil, false, errors.New("hiding Kubernetes secrets requires a dry-run mode")
|
||||
}
|
||||
|
||||
@@ -289,13 +280,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
// Determine whether or not to interact with remote
|
||||
var interactWithRemote bool
|
||||
if !u.isDryRun() || u.DryRunOption == "server" || u.DryRunOption == "none" || u.DryRunOption == "false" {
|
||||
interactWithRemote = true
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithRemote, u.EnableDNS, u.HideSecret)
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithServer(u.DryRunStrategy), u.EnableDNS, u.HideSecret)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
@@ -391,8 +376,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
||||
return nil
|
||||
})
|
||||
|
||||
// Run if it is a dry run
|
||||
if u.isDryRun() {
|
||||
if isDryRun(u.DryRunStrategy) {
|
||||
slog.Debug("dry run for release", "name", upgradedRelease.Name)
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
|
||||
@@ -545,7 +545,7 @@ func TestUpgradeRelease_DryRun(t *testing.T) {
|
||||
rel.Info.Status = release.StatusDeployed
|
||||
req.NoError(upAction.cfg.Releases.Create(rel))
|
||||
|
||||
upAction.DryRun = true
|
||||
upAction.DryRunStrategy = DryRunClient
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
ctx, done := context.WithCancel(t.Context())
|
||||
@@ -577,7 +577,7 @@ func TestUpgradeRelease_DryRun(t *testing.T) {
|
||||
is.Equal(1, lastRelease.Version)
|
||||
|
||||
// Ensure in a dry run mode when using HideSecret
|
||||
upAction.DryRun = false
|
||||
upAction.DryRunStrategy = DryRunNone
|
||||
vals = map[string]interface{}{}
|
||||
|
||||
ctx, done = context.WithCancel(t.Context())
|
||||
|
||||
83
pkg/cmd/helpers.go
Normal file
83
pkg/cmd/helpers.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"helm.sh/helm/v4/pkg/action"
|
||||
)
|
||||
|
||||
func addDryRunFlag(cmd *cobra.Command) {
|
||||
// --dry-run options with expected outcome:
|
||||
// - Not set means no dry run and server is contacted.
|
||||
// - Set with no value, a value of client, or a value of true and the server is not contacted
|
||||
// - Set with a value of false, none, or false and the server is contacted
|
||||
// The true/false part is meant to reflect some legacy behavior while none is equal to "".
|
||||
f := cmd.Flags()
|
||||
f.String(
|
||||
"dry-run",
|
||||
"none",
|
||||
`simulates the operation without persisting changes. Must be one of: "none" (default), "client", or "server". '--dry-run=none' executes the operation normally and persists changes (no simulation). '--dry-run=client' simulates the operation client-side only and avoids cluster connections. '--dry-run=server' simulates the operation on the server, requiring cluster connectivity.`)
|
||||
f.Lookup("dry-run").NoOptDefVal = "unset"
|
||||
}
|
||||
|
||||
// Determine the `action.DryRunStrategy` given -dry-run=<value>` flag (or absence of)
|
||||
// Legacy usage of the flag: boolean values, and `--dry-run` (without value) are supported, and log warnings emitted
|
||||
func cmdGetDryRunFlagStrategy(cmd *cobra.Command, isTemplate bool) (action.DryRunStrategy, error) {
|
||||
|
||||
f := cmd.Flag("dry-run")
|
||||
v := f.Value.String()
|
||||
|
||||
switch v {
|
||||
case f.NoOptDefVal:
|
||||
slog.Warn(`--dry-run is deprecated and should be replaced with '--dry-run=client'`)
|
||||
return action.DryRunClient, nil
|
||||
case string(action.DryRunClient):
|
||||
return action.DryRunClient, nil
|
||||
case string(action.DryRunServer):
|
||||
return action.DryRunServer, nil
|
||||
case string(action.DryRunNone):
|
||||
if isTemplate {
|
||||
// Special case hack for `helm template`, which is always a dry run
|
||||
return action.DryRunNone, fmt.Errorf(`invalid dry-run value (%q). Must be "server" or "client"`, v)
|
||||
}
|
||||
return action.DryRunNone, nil
|
||||
}
|
||||
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return action.DryRunNone, fmt.Errorf(`invalid dry-run value (%q). Must be "none", "server", or "client"`, v)
|
||||
}
|
||||
|
||||
if isTemplate && !b {
|
||||
// Special case for `helm template`, which is always a dry run
|
||||
return action.DryRunNone, fmt.Errorf(`invalid dry-run value (%q). Must be "server" or "client"`, v)
|
||||
}
|
||||
|
||||
result := action.DryRunNone
|
||||
if b {
|
||||
result = action.DryRunClient
|
||||
}
|
||||
slog.Warn(fmt.Sprintf(`boolean '--dry-run=%v' flag is deprecated and must be replaced with '--dry-run=%s'`, v, result))
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -18,7 +18,9 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -26,6 +28,8 @@ import (
|
||||
|
||||
shellwords "github.com/mattn/go-shellwords"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"helm.sh/helm/v4/internal/test"
|
||||
"helm.sh/helm/v4/pkg/action"
|
||||
@@ -151,3 +155,155 @@ func resetEnv() func() {
|
||||
settings = cli.New()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmdGetDryRunFlagStrategy(t *testing.T) {
|
||||
|
||||
type testCaseExpectedLog struct {
|
||||
Level string
|
||||
Msg string
|
||||
}
|
||||
testCases := map[string]struct {
|
||||
DryRunFlagArg string
|
||||
IsTemplate bool
|
||||
ExpectedStrategy action.DryRunStrategy
|
||||
ExpectedError bool
|
||||
ExpectedLog *testCaseExpectedLog
|
||||
}{
|
||||
"unset_value": {
|
||||
DryRunFlagArg: "--dry-run",
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `--dry-run is deprecated and should be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"unset_special": {
|
||||
DryRunFlagArg: "--dry-run=unset", // Special value that matches cmd.Flags("dry-run").NoOptDefVal
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `--dry-run is deprecated and should be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"none": {
|
||||
DryRunFlagArg: "--dry-run=none",
|
||||
ExpectedStrategy: action.DryRunNone,
|
||||
},
|
||||
"client": {
|
||||
DryRunFlagArg: "--dry-run=client",
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
},
|
||||
"server": {
|
||||
DryRunFlagArg: "--dry-run=server",
|
||||
ExpectedStrategy: action.DryRunServer,
|
||||
},
|
||||
"bool_false": {
|
||||
DryRunFlagArg: "--dry-run=false",
|
||||
ExpectedStrategy: action.DryRunNone,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `boolean '--dry-run=false' flag is deprecated and must be replaced with '--dry-run=none'`,
|
||||
},
|
||||
},
|
||||
"bool_true": {
|
||||
DryRunFlagArg: "--dry-run=true",
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `boolean '--dry-run=true' flag is deprecated and must be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"bool_0": {
|
||||
DryRunFlagArg: "--dry-run=0",
|
||||
ExpectedStrategy: action.DryRunNone,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `boolean '--dry-run=0' flag is deprecated and must be replaced with '--dry-run=none'`,
|
||||
},
|
||||
},
|
||||
"bool_1": {
|
||||
DryRunFlagArg: "--dry-run=1",
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `boolean '--dry-run=1' flag is deprecated and must be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"invalid": {
|
||||
DryRunFlagArg: "--dry-run=invalid",
|
||||
ExpectedError: true,
|
||||
},
|
||||
"template_unset_value": {
|
||||
DryRunFlagArg: "--dry-run",
|
||||
IsTemplate: true,
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `--dry-run is deprecated and should be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"template_bool_false": {
|
||||
DryRunFlagArg: "--dry-run=false",
|
||||
IsTemplate: true,
|
||||
ExpectedError: true,
|
||||
},
|
||||
"template_bool_template_true": {
|
||||
DryRunFlagArg: "--dry-run=true",
|
||||
IsTemplate: true,
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
ExpectedLog: &testCaseExpectedLog{
|
||||
Level: "WARN",
|
||||
Msg: `boolean '--dry-run=true' flag is deprecated and must be replaced with '--dry-run=client'`,
|
||||
},
|
||||
},
|
||||
"template_none": {
|
||||
DryRunFlagArg: "--dry-run=none",
|
||||
IsTemplate: true,
|
||||
ExpectedError: true,
|
||||
},
|
||||
"template_client": {
|
||||
DryRunFlagArg: "--dry-run=client",
|
||||
IsTemplate: true,
|
||||
ExpectedStrategy: action.DryRunClient,
|
||||
},
|
||||
"template_server": {
|
||||
DryRunFlagArg: "--dry-run=server",
|
||||
IsTemplate: true,
|
||||
ExpectedStrategy: action.DryRunServer,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
|
||||
logBuf := new(bytes.Buffer)
|
||||
logger := slog.New(slog.NewJSONHandler(logBuf, nil))
|
||||
slog.SetDefault(logger)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "helm",
|
||||
}
|
||||
addDryRunFlag(cmd)
|
||||
cmd.Flags().Parse([]string{"helm", tc.DryRunFlagArg})
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, tc.IsTemplate)
|
||||
if tc.ExpectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.ExpectedStrategy, dryRunStrategy)
|
||||
}
|
||||
|
||||
if tc.ExpectedLog != nil {
|
||||
logResult := map[string]string{}
|
||||
err = json.Unmarshal(logBuf.Bytes(), &logResult)
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.Equal(t, tc.ExpectedLog.Level, logResult["level"])
|
||||
assert.Equal(t, tc.ExpectedLog.Msg, logResult["msg"])
|
||||
} else {
|
||||
assert.Equal(t, 0, logBuf.Len())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"slices"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -144,7 +142,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstall(args, toComplete, client)
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
|
||||
if err != nil {
|
||||
@@ -152,12 +150,12 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
// This is for the case where "" is specifically passed in as a
|
||||
// value. When there is no value passed in NoOptDefVal will be used
|
||||
// and it is set to client. See addInstallFlags.
|
||||
if client.DryRunOption == "" {
|
||||
client.DryRunOption = "none"
|
||||
dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.DryRunStrategy = dryRunStrategy
|
||||
|
||||
rel, err := runInstall(args, client, valueOpts, out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("INSTALLATION FAILED: %w", err)
|
||||
@@ -173,11 +171,12 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
|
||||
f := cmd.Flags()
|
||||
addInstallFlags(cmd, f, client, valueOpts)
|
||||
// hide-secret is not available in all places the install flags are used so
|
||||
// it is added separately
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
|
||||
addDryRunFlag(cmd)
|
||||
bindOutputFlag(cmd, &outfmt)
|
||||
bindPostRenderFlag(cmd, &client.PostRenderer, settings)
|
||||
|
||||
@@ -186,13 +185,6 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
|
||||
func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
|
||||
f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
|
||||
// --dry-run options with expected outcome:
|
||||
// - Not set means no dry run and server is contacted.
|
||||
// - Set with no value, a value of client, or a value of true and the server is not contacted
|
||||
// - Set with a value of false, none, or false and the server is contacted
|
||||
// The true/false part is meant to reflect some legacy behavior while none is equal to "".
|
||||
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.ForceReplace, "force-replace", false, "force resource updates by replacement")
|
||||
f.BoolVar(&client.ForceReplace, "force", false, "deprecated")
|
||||
f.MarkDeprecated("force", "use --force-replace instead")
|
||||
@@ -316,11 +308,6 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
|
||||
|
||||
client.Namespace = settings.Namespace()
|
||||
|
||||
// Validate DryRunOption member is one of the allowed values
|
||||
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create context and prepare the handle of SIGTERM
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
@@ -363,13 +350,3 @@ func compInstall(args []string, toComplete string, client *action.Install) ([]st
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
|
||||
// Validate dry-run flag value with a set of allowed value
|
||||
allowedDryRunValues := []string{"false", "true", "none", "client", "server"}
|
||||
isAllowed := slices.Contains(allowedDryRunValues, dryRunOptionFlagValue)
|
||||
if !isAllowed {
|
||||
return errors.New("invalid dry-run flag. Flag must one of the following: false, true, none, client, server")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
ver, err := strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
@@ -66,6 +66,12 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client.Version = ver
|
||||
}
|
||||
|
||||
dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.DryRunStrategy = dryRunStrategy
|
||||
|
||||
if err := client.Run(args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -76,7 +82,6 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate a rollback")
|
||||
f.BoolVar(&client.ForceReplace, "force-replace", false, "force resource updates by replacement")
|
||||
f.BoolVar(&client.ForceReplace, "force", false, "deprecated")
|
||||
f.MarkDeprecated("force", "use --force-replace instead")
|
||||
@@ -87,6 +92,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
|
||||
f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback fails")
|
||||
f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
|
||||
addDryRunFlag(cmd)
|
||||
AddWaitFlag(cmd, &client.WaitStrategy)
|
||||
cmd.MarkFlagsMutuallyExclusive("force-replace", "force-conflicts")
|
||||
cmd.MarkFlagsMutuallyExclusive("force", "force-conflicts")
|
||||
|
||||
@@ -66,7 +66,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstall(args, toComplete, client)
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if kubeVersion != "" {
|
||||
parsedKubeVersion, err := common.ParseKubeVersion(kubeVersion)
|
||||
if err != nil {
|
||||
@@ -82,16 +82,17 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
// This is for the case where "" is specifically passed in as a
|
||||
// value. When there is no value passed in NoOptDefVal will be used
|
||||
// and it is set to client. See addInstallFlags.
|
||||
if client.DryRunOption == "" {
|
||||
client.DryRunOption = "true"
|
||||
dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.DryRun = true
|
||||
if validate {
|
||||
// Mimic deprecated --validate flag behavior by enabling server dry run
|
||||
dryRunStrategy = action.DryRunServer
|
||||
}
|
||||
client.DryRunStrategy = dryRunStrategy
|
||||
client.ReleaseName = "release-name"
|
||||
client.Replace = true // Skip the name check
|
||||
client.ClientOnly = !validate
|
||||
client.APIVersions = common.VersionSet(extraAPIs)
|
||||
client.IncludeCRDs = includeCrds
|
||||
rel, err := runInstall(args, client, valueOpts, out)
|
||||
@@ -195,14 +196,21 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
addInstallFlags(cmd, f, client, valueOpts)
|
||||
f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates")
|
||||
f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
|
||||
f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install")
|
||||
f.BoolVar(&validate, "validate", false, "deprecated")
|
||||
f.MarkDeprecated("validate", "use '--dry-run=server' instead")
|
||||
f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output")
|
||||
f.BoolVar(&skipTests, "skip-tests", false, "skip tests from templated output")
|
||||
f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
|
||||
f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for Capabilities.KubeVersion")
|
||||
f.StringSliceVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions (multiple can be specified)")
|
||||
f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.")
|
||||
f.String(
|
||||
"dry-run",
|
||||
"client",
|
||||
`simulates the operation either client-side or server-side. Must be either: "client", or "server". '--dry-run=client simulates the operation client-side only and avoids cluster connections. '--dry-run=server' simulates/validates the operation on the server, requiring cluster connectivity.`)
|
||||
f.Lookup("dry-run").NoOptDefVal = "unset"
|
||||
bindPostRenderFlag(cmd, &client.PostRenderer, settings)
|
||||
cmd.MarkFlagsMutuallyExclusive("validate", "dry-run")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
}
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client.Namespace = settings.Namespace()
|
||||
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
@@ -110,12 +110,12 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
// This is for the case where "" is specifically passed in as a
|
||||
// value. When there is no value passed in NoOptDefVal will be used
|
||||
// and it is set to client. See addInstallFlags.
|
||||
if client.DryRunOption == "" {
|
||||
client.DryRunOption = "none"
|
||||
dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client.DryRunStrategy = dryRunStrategy
|
||||
|
||||
// Fixes #7002 - Support reading values from STDIN for `upgrade` command
|
||||
// Must load values AFTER determining if we have to call install so that values loaded from stdin are not read twice
|
||||
if client.Install {
|
||||
@@ -132,8 +132,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
instClient.CreateNamespace = createNamespace
|
||||
instClient.ChartPathOptions = client.ChartPathOptions
|
||||
instClient.ForceReplace = client.ForceReplace
|
||||
instClient.DryRun = client.DryRun
|
||||
instClient.DryRunOption = client.DryRunOption
|
||||
instClient.DryRunStrategy = client.DryRunStrategy
|
||||
instClient.DisableHooks = client.DisableHooks
|
||||
instClient.SkipCRDs = client.SkipCRDs
|
||||
instClient.Timeout = client.Timeout
|
||||
@@ -183,10 +182,6 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate dry-run flag value is one of the allowed values
|
||||
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := getter.All(settings)
|
||||
vals, err := valueOpts.MergeValues(p)
|
||||
@@ -274,9 +269,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present")
|
||||
f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
|
||||
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
|
||||
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
|
||||
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
|
||||
f.Lookup("dry-run").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.ForceReplace, "force-replace", false, "force resource updates by replacement")
|
||||
f.BoolVar(&client.ForceReplace, "force", false, "deprecated")
|
||||
f.MarkDeprecated("force", "use --force-replace instead")
|
||||
@@ -303,6 +296,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
|
||||
f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
|
||||
f.BoolVar(&client.TakeOwnership, "take-ownership", false, "if set, upgrade will ignore the check for helm annotations and take ownership of the existing resources")
|
||||
addDryRunFlag(cmd)
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
addValueOptionsFlags(f, valueOpts)
|
||||
bindOutputFlag(cmd, &outfmt)
|
||||
|
||||
Reference in New Issue
Block a user