mirror of https://github.com/helm/helm.git
feat(helm): add ability for a dry-run to evaluate lookup functions
When a helm command is run with the --dry-run-option=server flag, it will try to connect to the cluster to be able to render lookup functions. Closes #8137 Signed-off-by: Tapas Kapadia <tapaskapadia10@gmail.com>
This commit is contained in:
parent
be99ebe8af
commit
ddb33580db
|
@ -154,8 +154,9 @@ 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")
|
||||
f.StringVar(&client.DryRun, "dry-run", "none", "simulate an install. If --dry-run is set with no option being specified or as 'client', it will not attempt cluster connections. Setting option as 'server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
|
||||
f.StringVar(&client.DryRunOption, "dry-run-option", "none", "simulate an install. If --dry-run is set with no option being specified or as 'client', it will not attempt cluster connections. Setting option as 'server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run-option").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
|
||||
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
|
||||
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
|
||||
|
@ -174,6 +175,8 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
|
|||
addValueOptionsFlags(f, valueOpts)
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
|
||||
cmd.MarkFlagsMutuallyExclusive("dry-run", "dry-run-option")
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
requiredArgs := 2
|
||||
if client.GenerateName {
|
||||
|
@ -263,7 +266,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
|
|||
client.Namespace = settings.Namespace()
|
||||
|
||||
// validate dry-run flag value is one of the allowed values
|
||||
if err := validateDryRunFlag(client.DryRun); err != nil {
|
||||
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -308,12 +311,12 @@ func compInstall(args []string, toComplete string, client *action.Install) ([]st
|
|||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func validateDryRunFlag(dryRunFlagValue string) error {
|
||||
func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
|
||||
// validate dry-run flag value with set of allowed value
|
||||
allowedDryRunValues := []string{"false", "true", "none", "client", "server"}
|
||||
isAllowed := false
|
||||
for _, v := range allowedDryRunValues {
|
||||
if dryRunFlagValue == v {
|
||||
if dryRunOptionFlagValue == v {
|
||||
isAllowed = true
|
||||
break
|
||||
}
|
||||
|
|
|
@ -73,7 +73,6 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
client.KubeVersion = parsedKubeVersion
|
||||
}
|
||||
|
||||
client.DryRun = "client"
|
||||
client.ReleaseName = "release-name"
|
||||
client.Replace = true // Skip the name check
|
||||
client.ClientOnly = !validate
|
||||
|
|
|
@ -106,6 +106,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
instClient.ChartPathOptions = client.ChartPathOptions
|
||||
instClient.Force = client.Force
|
||||
instClient.DryRun = client.DryRun
|
||||
instClient.DryRunOption = client.DryRunOption
|
||||
instClient.DisableHooks = client.DisableHooks
|
||||
instClient.SkipCRDs = client.SkipCRDs
|
||||
instClient.Timeout = client.Timeout
|
||||
|
@ -119,7 +120,6 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
instClient.SubNotes = client.SubNotes
|
||||
instClient.Description = client.Description
|
||||
instClient.DependencyUpdate = client.DependencyUpdate
|
||||
|
||||
rel, err := runInstall(args, instClient, valueOpts, out)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -140,7 +140,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
return err
|
||||
}
|
||||
// validate dry-run flag value is one of the allowed values
|
||||
if err := validateDryRunFlag(client.DryRun); err != nil {
|
||||
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -218,8 +218,9 @@ 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.DryRun, "dry-run", "none", "simulate an install. If --dry-run is set with no option being specified or as 'client', it will not attempt cluster connections. Setting option as 'server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade")
|
||||
f.StringVar(&client.DryRunOption, "dry-run-option", "none", "simulate an install. If --dry-run is set with no option being specified or as 'client', it will not attempt cluster connections. Setting option as 'server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run-option").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
|
||||
f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
|
||||
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
|
||||
|
@ -242,6 +243,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
bindOutputFlag(cmd, &outfmt)
|
||||
bindPostRenderFlag(cmd, &client.PostRenderer)
|
||||
|
||||
cmd.MarkFlagsMutuallyExclusive("dry-run", "dry-run-option")
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 2 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
|
|
|
@ -102,7 +102,7 @@ type Configuration struct {
|
|||
// TODO: This function is badly in need of a refactor.
|
||||
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
|
||||
// This code has to do with writing files to disk.
|
||||
func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun string) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, interactWithRemote bool) ([]*release.Hook, *bytes.Buffer, string, error) {
|
||||
hs := []*release.Hook{}
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
|
@ -121,11 +121,11 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
|
|||
var err2 error
|
||||
|
||||
// A `helm template` should not talk to the remote cluster. However, commands
|
||||
// with `--dry-run` with the value of false, none, or sever should try to connect to the cluster.
|
||||
// This enables the ability to render 'lookup' functions.
|
||||
// with the flag `--dry-run-option` with the value of false, none, or sever
|
||||
// or with the flag `--dry-run` with the value of false should try to interact with the cluster.
|
||||
// It may break in interesting and exotic ways because other data (e.g. discovery)
|
||||
// is mocked.
|
||||
if (dryRun == "server" || dryRun == "none" || dryRun == "false") && cfg.RESTClientGetter != nil {
|
||||
if interactWithRemote && cfg.RESTClientGetter != nil {
|
||||
restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
|
||||
if err != nil {
|
||||
return hs, b, "", err
|
||||
|
|
|
@ -71,7 +71,8 @@ type Install struct {
|
|||
ClientOnly bool
|
||||
Force bool
|
||||
CreateNamespace bool
|
||||
DryRun string
|
||||
DryRun bool
|
||||
DryRunOption string
|
||||
DisableHooks bool
|
||||
Replace bool
|
||||
Wait bool
|
||||
|
@ -128,8 +129,6 @@ type ChartPathOptions struct {
|
|||
func NewInstall(cfg *Configuration) *Install {
|
||||
in := &Install{
|
||||
cfg: cfg,
|
||||
// Set default value of DryRun for before flags are binded (tests)
|
||||
DryRun: "none",
|
||||
}
|
||||
in.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
|
||||
|
@ -205,11 +204,21 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// determine dry run behavior
|
||||
if i.DryRun || i.DryRunOption == "client" || i.DryRunOption == "server" || i.DryRunOption == "true" {
|
||||
i.DryRun = true
|
||||
}
|
||||
|
||||
var interactWithRemote bool
|
||||
if !i.DryRun || i.DryRunOption == "server" {
|
||||
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 {
|
||||
// On dry run, bail here
|
||||
if i.DryRun != "none" && i.DryRun != "false" {
|
||||
if i.DryRun {
|
||||
i.cfg.Log("WARNING: 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
|
||||
|
@ -243,7 +252,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
}
|
||||
|
||||
// special case for helm template --is-upgrade
|
||||
isUpgrade := i.IsUpgrade && (i.DryRun != "none" && i.DryRun != "false")
|
||||
isUpgrade := i.IsUpgrade && i.DryRun
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: i.ReleaseName,
|
||||
Namespace: i.Namespace,
|
||||
|
@ -259,8 +268,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
rel := i.createRelease(chrt, vals)
|
||||
|
||||
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, i.DryRun)
|
||||
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)
|
||||
// Even for errors, attach this if available
|
||||
if manifestDoc != nil {
|
||||
rel.Manifest = manifestDoc.String()
|
||||
|
@ -301,7 +309,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
}
|
||||
|
||||
// Bail out here if it is a dry run
|
||||
if i.DryRun != "none" && i.DryRun != "false" {
|
||||
if i.DryRun {
|
||||
rel.Info.Description = "Dry run complete"
|
||||
return rel, nil
|
||||
}
|
||||
|
@ -471,7 +479,7 @@ func (i *Install) availableName() error {
|
|||
return errors.Wrapf(err, "release name %q", start)
|
||||
}
|
||||
// On dry run, bail here
|
||||
if i.DryRun != "none" && i.DryRun != "false" {
|
||||
if i.DryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
|
|||
func TestInstallRelease_DryRun(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRun = "true"
|
||||
instAction.DryRun = true
|
||||
vals := map[string]interface{}{}
|
||||
res, err := instAction.Run(buildChart(withSampleTemplates()), vals)
|
||||
if err != nil {
|
||||
|
@ -258,7 +258,7 @@ func TestInstallRelease_DryRun(t *testing.T) {
|
|||
func TestInstallRelease_DryRun_Lookup(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.DryRun = "true"
|
||||
instAction.DryRun = true
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
mockChart := buildChart(withSampleTemplates())
|
||||
|
@ -278,7 +278,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.DryRun = true
|
||||
vals := map[string]interface{}{}
|
||||
_, err := instAction.Run(buildChart(withSampleIncludingIncorrectTemplates()), vals)
|
||||
expectedErr := "\"hello/templates/incorrect\" at <.Values.bad.doh>: nil pointer evaluating interface {}.doh"
|
||||
|
|
|
@ -70,7 +70,9 @@ type Upgrade struct {
|
|||
// DisableHooks disables hook processing if set to true.
|
||||
DisableHooks bool
|
||||
// DryRun controls whether the operation is prepared, but not executed.
|
||||
DryRun string
|
||||
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
|
||||
// Force will, if set to `true`, ignore certain warnings and perform the upgrade anyway.
|
||||
//
|
||||
// This should be used with caution.
|
||||
|
@ -113,8 +115,6 @@ type resultMessage struct {
|
|||
func NewUpgrade(cfg *Configuration) *Upgrade {
|
||||
up := &Upgrade{
|
||||
cfg: cfg,
|
||||
// Set default value of DryRun for before flags are binded (tests)
|
||||
DryRun: "none",
|
||||
}
|
||||
up.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
|
||||
|
@ -140,6 +140,12 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
|||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, errors.Errorf("release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
// determine dry run behavior
|
||||
if u.DryRun || u.DryRunOption == "client" || u.DryRunOption == "server" || u.DryRunOption == "true" {
|
||||
u.DryRun = true
|
||||
}
|
||||
|
||||
u.cfg.Log("preparing upgrade for %s", name)
|
||||
currentRelease, upgradedRelease, err := u.prepareUpgrade(name, chart, vals)
|
||||
if err != nil {
|
||||
|
@ -153,8 +159,9 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
|||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Do not update for dry runs
|
||||
if u.DryRun == "none" || u.DryRun == "false" {
|
||||
if !u.DryRun {
|
||||
u.cfg.Log("updating status for upgraded release for %s", name)
|
||||
if err := u.cfg.Releases.Update(upgradedRelease); err != nil {
|
||||
return res, err
|
||||
|
@ -232,7 +239,13 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
|
||||
// determine whether or not to interact with remote
|
||||
var interactWithRemote bool
|
||||
if !u.DryRun || u.DryRunOption == "server" {
|
||||
interactWithRemote = true
|
||||
}
|
||||
|
||||
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithRemote)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -311,7 +324,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
|||
})
|
||||
|
||||
// Run if it is a dry run
|
||||
if u.DryRun != "none" && u.DryRun != "false" {
|
||||
if u.DryRun {
|
||||
u.cfg.Log("dry run for %s", upgradedRelease.Name)
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
|
|
Loading…
Reference in New Issue