github.com/Beeketing/helm@v2.12.1+incompatible/cmd/helm/upgrade.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"strings"
    23  
    24  	"github.com/spf13/cobra"
    25  
    26  	"k8s.io/helm/pkg/chartutil"
    27  	"k8s.io/helm/pkg/helm"
    28  	"k8s.io/helm/pkg/renderutil"
    29  	storageerrors "k8s.io/helm/pkg/storage/errors"
    30  )
    31  
    32  const upgradeDesc = `
    33  This command upgrades a release to a specified version of a chart and/or updates chart values.
    34  
    35  Required arguments are release and chart. The chart argument can be one of:
    36   - a chart reference('stable/mariadb'); use '--version' and '--devel' flags for versions other than latest,
    37   - a path to a chart directory,
    38   - a packaged chart,
    39   - a fully qualified URL.
    40  
    41  To customize the chart values, use any of
    42   - '--values'/'-f' to pass in a yaml file holding settings,
    43   - '--set' to provide one or more key=val pairs directly,
    44   - '--set-string' to provide key=val forcing val to be stored as a string,
    45   - '--set-file' to provide key=path to read a single large value from a file at path.
    46  
    47  To edit or append to the existing customized values, add the 
    48   '--reuse-values' flag, otherwise any existing customized values are ignored.
    49  
    50  If no chart value arguments are provided on the command line, any existing customized values are carried
    51  forward. If you want to revert to just the values provided in the chart, use the '--reset-values' flag.
    52  
    53  You can specify any of the chart value flags multiple times. The priority will be given to the last
    54  (right-most) value specified. For example, if both myvalues.yaml and override.yaml contained a key
    55  called 'Test', the value set in override.yaml would take precedence:
    56  
    57  	$ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis
    58  
    59  Note that the key name provided to the '--set', '--set-string' and '--set-file' flags can reference
    60  structure elements. Examples:
    61    - mybool=TRUE
    62    - livenessProbe.timeoutSeconds=10
    63    - metrics.annotations[0]=hey,metrics.annotations[1]=ho
    64  
    65  which sets the top level key mybool to true, the nested timeoutSeconds to 10, and two array values, respectively.
    66  
    67  Note that the value side of the key=val provided to '--set' and '--set-string' flags will pass through
    68  shell evaluation followed by yaml type parsing to produce the final value. This may alter inputs with
    69  special characters in unexpected ways, for example
    70  
    71  	$ helm upgrade --set pwd=3jk$o2,z=f\30.e redis ./redis
    72  
    73  results in "pwd: 3jk" and "z: f30.e". Use single quotes to avoid shell evaluation and argument delimiters,
    74  and use backslash to escape yaml special characters:
    75  
    76  	$ helm upgrade --set pwd='3jk$o2z=f\\30.e' redis ./redis
    77  
    78  which results in the expected "pwd: 3jk$o2z=f\30.e". If a single quote occurs in your value then follow
    79  your shell convention for escaping it; for example in bash:
    80  
    81  	$ helm upgrade --set pwd='3jk$o2z=f\\30with'\''quote'
    82  
    83  which results in "pwd: 3jk$o2z=f\30with'quote".
    84  `
    85  
    86  type upgradeCmd struct {
    87  	release      string
    88  	chart        string
    89  	out          io.Writer
    90  	client       helm.Interface
    91  	dryRun       bool
    92  	recreate     bool
    93  	force        bool
    94  	disableHooks bool
    95  	valueFiles   valueFiles
    96  	values       []string
    97  	stringValues []string
    98  	fileValues   []string
    99  	verify       bool
   100  	keyring      string
   101  	install      bool
   102  	namespace    string
   103  	version      string
   104  	timeout      int64
   105  	resetValues  bool
   106  	reuseValues  bool
   107  	wait         bool
   108  	repoURL      string
   109  	username     string
   110  	password     string
   111  	devel        bool
   112  	description  string
   113  
   114  	certFile string
   115  	keyFile  string
   116  	caFile   string
   117  }
   118  
   119  func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
   120  
   121  	upgrade := &upgradeCmd{
   122  		out:    out,
   123  		client: client,
   124  	}
   125  
   126  	cmd := &cobra.Command{
   127  		Use:     "upgrade [RELEASE] [CHART]",
   128  		Short:   "upgrade a release",
   129  		Long:    upgradeDesc,
   130  		PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
   131  		RunE: func(cmd *cobra.Command, args []string) error {
   132  			if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
   133  				return err
   134  			}
   135  
   136  			if upgrade.version == "" && upgrade.devel {
   137  				debug("setting version to >0.0.0-0")
   138  				upgrade.version = ">0.0.0-0"
   139  			}
   140  
   141  			upgrade.release = args[0]
   142  			upgrade.chart = args[1]
   143  			upgrade.client = ensureHelmClient(upgrade.client)
   144  
   145  			return upgrade.run()
   146  		},
   147  	}
   148  
   149  	f := cmd.Flags()
   150  	settings.AddFlagsTLS(f)
   151  	f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
   152  	f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
   153  	f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
   154  	f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed")
   155  	f.StringArrayVar(&upgrade.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   156  	f.StringArrayVar(&upgrade.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   157  	f.StringArrayVar(&upgrade.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
   158  	f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
   159  	f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
   160  	f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
   161  	f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys")
   162  	f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
   163  	f.StringVar(&upgrade.namespace, "namespace", "", "namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace")
   164  	f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
   165  	f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
   166  	f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
   167  	f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.")
   168  	f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
   169  	f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart")
   170  	f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart")
   171  	f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart")
   172  	f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
   173  	f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
   174  	f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
   175  	f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
   176  	f.StringVar(&upgrade.description, "description", "", "specify the description to use for the upgrade, rather than the default")
   177  
   178  	f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
   179  
   180  	// set defaults from environment
   181  	settings.InitTLS(f)
   182  
   183  	return cmd
   184  }
   185  
   186  func (u *upgradeCmd) run() error {
   187  	chartPath, err := locateChartPath(u.repoURL, u.username, u.password, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	if u.install {
   193  		// If a release does not exist, install it. If another error occurs during
   194  		// the check, ignore the error and continue with the upgrade.
   195  		//
   196  		// The returned error is a grpc.rpcError that wraps the message from the original error.
   197  		// So we're stuck doing string matching against the wrapped error, which is nested somewhere
   198  		// inside of the grpc.rpcError message.
   199  		releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
   200  
   201  		if err == nil {
   202  			if u.namespace == "" {
   203  				u.namespace = defaultNamespace()
   204  			}
   205  			previousReleaseNamespace := releaseHistory.Releases[0].Namespace
   206  			if previousReleaseNamespace != u.namespace {
   207  				fmt.Fprintf(u.out,
   208  					"WARNING: Namespace %q doesn't match with previous. Release will be deployed to %s\n",
   209  					u.namespace, previousReleaseNamespace,
   210  				)
   211  			}
   212  		}
   213  
   214  		if err != nil && strings.Contains(err.Error(), storageerrors.ErrReleaseNotFound(u.release).Error()) {
   215  			fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release)
   216  			ic := &installCmd{
   217  				chartPath:    chartPath,
   218  				client:       u.client,
   219  				out:          u.out,
   220  				name:         u.release,
   221  				valueFiles:   u.valueFiles,
   222  				dryRun:       u.dryRun,
   223  				verify:       u.verify,
   224  				disableHooks: u.disableHooks,
   225  				keyring:      u.keyring,
   226  				values:       u.values,
   227  				stringValues: u.stringValues,
   228  				fileValues:   u.fileValues,
   229  				namespace:    u.namespace,
   230  				timeout:      u.timeout,
   231  				wait:         u.wait,
   232  				description:  u.description,
   233  			}
   234  			return ic.run()
   235  		}
   236  	}
   237  
   238  	rawVals, err := vals(u.valueFiles, u.values, u.stringValues, u.fileValues, u.certFile, u.keyFile, u.caFile)
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	// Check chart requirements to make sure all dependencies are present in /charts
   244  	if ch, err := chartutil.Load(chartPath); err == nil {
   245  		if req, err := chartutil.LoadRequirements(ch); err == nil {
   246  			if err := renderutil.CheckDependencies(ch, req); err != nil {
   247  				return err
   248  			}
   249  		} else if err != chartutil.ErrRequirementsNotFound {
   250  			return fmt.Errorf("cannot load requirements: %v", err)
   251  		}
   252  	} else {
   253  		return prettyError(err)
   254  	}
   255  
   256  	resp, err := u.client.UpdateRelease(
   257  		u.release,
   258  		chartPath,
   259  		helm.UpdateValueOverrides(rawVals),
   260  		helm.UpgradeDryRun(u.dryRun),
   261  		helm.UpgradeRecreate(u.recreate),
   262  		helm.UpgradeForce(u.force),
   263  		helm.UpgradeDisableHooks(u.disableHooks),
   264  		helm.UpgradeTimeout(u.timeout),
   265  		helm.ResetValues(u.resetValues),
   266  		helm.ReuseValues(u.reuseValues),
   267  		helm.UpgradeWait(u.wait),
   268  		helm.UpgradeDescription(u.description))
   269  	if err != nil {
   270  		return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
   271  	}
   272  
   273  	if settings.Debug {
   274  		printRelease(u.out, resp.Release)
   275  	}
   276  
   277  	fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)
   278  
   279  	// Print the status like status command does
   280  	status, err := u.client.ReleaseStatus(u.release)
   281  	if err != nil {
   282  		return prettyError(err)
   283  	}
   284  	PrintStatus(u.out, status)
   285  
   286  	return nil
   287  }