github.com/zoumo/helm@v2.5.0+incompatible/cmd/helm/upgrade.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  	"io/ioutil"
    23  	"strings"
    24  
    25  	"github.com/ghodss/yaml"
    26  	"github.com/spf13/cobra"
    27  
    28  	"k8s.io/helm/pkg/chartutil"
    29  	"k8s.io/helm/pkg/helm"
    30  	"k8s.io/helm/pkg/storage/driver"
    31  	"k8s.io/helm/pkg/strvals"
    32  )
    33  
    34  const upgradeDesc = `
    35  This command upgrades a release to a new version of a chart.
    36  
    37  The upgrade arguments must be a release and chart. The chart
    38  argument can be either: a chart reference('stable/mariadb'), a path to a chart directory,
    39  a packaged chart, or a fully qualified URL. For chart references, the latest
    40  version will be specified unless the '--version' flag is set.
    41  
    42  To override values in a chart, use either the '--values' flag and pass in a file
    43  or use the '--set' flag and pass configuration from the command line.
    44  
    45  You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
    46  last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
    47  contained a key called 'Test', the value set in override.yaml would take precedence:
    48  
    49  	$ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis
    50  
    51  You can specify the '--set' flag multiple times. The priority will be given to the
    52  last (right-most) set specified. For example, if both 'bar' and 'newbar' values are
    53  set for a key called 'foo', the 'newbar' value would take precedence:
    54  
    55  	$ helm upgrade --set foo=bar --set foo=newbar redis ./redis
    56  `
    57  
    58  type upgradeCmd struct {
    59  	release      string
    60  	chart        string
    61  	out          io.Writer
    62  	client       helm.Interface
    63  	dryRun       bool
    64  	recreate     bool
    65  	force        bool
    66  	disableHooks bool
    67  	valueFiles   valueFiles
    68  	values       []string
    69  	verify       bool
    70  	keyring      string
    71  	install      bool
    72  	namespace    string
    73  	version      string
    74  	timeout      int64
    75  	resetValues  bool
    76  	reuseValues  bool
    77  	wait         bool
    78  	repoURL      string
    79  	devel        bool
    80  	annotations  []string
    81  
    82  	certFile string
    83  	keyFile  string
    84  	caFile   string
    85  }
    86  
    87  func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
    88  
    89  	upgrade := &upgradeCmd{
    90  		out:    out,
    91  		client: client,
    92  	}
    93  
    94  	cmd := &cobra.Command{
    95  		Use:     "upgrade [RELEASE] [CHART]",
    96  		Short:   "upgrade a release",
    97  		Long:    upgradeDesc,
    98  		PreRunE: setupConnection,
    99  		RunE: func(cmd *cobra.Command, args []string) error {
   100  			if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
   101  				return err
   102  			}
   103  
   104  			if upgrade.version == "" && upgrade.devel {
   105  				debug("setting version to >0.0.0-a")
   106  				upgrade.version = ">0.0.0-a"
   107  			}
   108  
   109  			upgrade.release = args[0]
   110  			upgrade.chart = args[1]
   111  			upgrade.client = ensureHelmClient(upgrade.client)
   112  
   113  			return upgrade.run()
   114  		},
   115  	}
   116  
   117  	f := cmd.Flags()
   118  	f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
   119  	f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
   120  	f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
   121  	f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed")
   122  	f.StringArrayVar(&upgrade.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   123  	f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
   124  	f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
   125  	f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
   126  	f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys")
   127  	f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
   128  	f.StringVar(&upgrade.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)")
   129  	f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
   130  	f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)")
   131  	f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
   132  	f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored.")
   133  	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")
   134  	f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart")
   135  	f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
   136  	f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
   137  	f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
   138  	f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored.")
   139  	f.StringArrayVar(&upgrade.annotations, "annotations", []string{}, "set release annotations (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   140  
   141  	f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
   142  
   143  	return cmd
   144  }
   145  
   146  func (u *upgradeCmd) run() error {
   147  	chartPath, err := locateChartPath(u.repoURL, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	if u.install {
   153  		// If a release does not exist, install it. If another error occurs during
   154  		// the check, ignore the error and continue with the upgrade.
   155  		//
   156  		// The returned error is a grpc.rpcError that wraps the message from the original error.
   157  		// So we're stuck doing string matching against the wrapped error, which is nested somewhere
   158  		// inside of the grpc.rpcError message.
   159  		_, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
   160  		if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound(u.release).Error()) {
   161  			fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release)
   162  			ic := &installCmd{
   163  				chartPath:    chartPath,
   164  				client:       u.client,
   165  				out:          u.out,
   166  				name:         u.release,
   167  				valueFiles:   u.valueFiles,
   168  				dryRun:       u.dryRun,
   169  				verify:       u.verify,
   170  				disableHooks: u.disableHooks,
   171  				keyring:      u.keyring,
   172  				values:       u.values,
   173  				namespace:    u.namespace,
   174  				timeout:      u.timeout,
   175  				wait:         u.wait,
   176  				annotations:  u.annotations,
   177  			}
   178  			return ic.run()
   179  		}
   180  	}
   181  
   182  	rawVals, err := u.vals()
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	annotations, err := parseAnnotations(u.annotations)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	// Check chart requirements to make sure all dependencies are present in /charts
   193  	if ch, err := chartutil.Load(chartPath); err == nil {
   194  		if req, err := chartutil.LoadRequirements(ch); err == nil {
   195  			if err := checkDependencies(ch, req); err != nil {
   196  				return err
   197  			}
   198  		} else if err != chartutil.ErrRequirementsNotFound {
   199  			return fmt.Errorf("cannot load requirements: %v", err)
   200  		}
   201  	} else {
   202  		return prettyError(err)
   203  	}
   204  
   205  	resp, err := u.client.UpdateRelease(
   206  		u.release,
   207  		chartPath,
   208  		helm.UpdateValueOverrides(rawVals),
   209  		helm.UpgradeDryRun(u.dryRun),
   210  		helm.UpgradeRecreate(u.recreate),
   211  		helm.UpgradeForce(u.force),
   212  		helm.UpgradeDisableHooks(u.disableHooks),
   213  		helm.UpgradeTimeout(u.timeout),
   214  		helm.ResetValues(u.resetValues),
   215  		helm.ReuseValues(u.reuseValues),
   216  		helm.UpgradeWait(u.wait),
   217  		helm.UpgradeAnnotations(annotations))
   218  	if err != nil {
   219  		return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
   220  	}
   221  
   222  	if settings.Debug {
   223  		printRelease(u.out, resp.Release)
   224  	}
   225  
   226  	fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)
   227  
   228  	// Print the status like status command does
   229  	status, err := u.client.ReleaseStatus(u.release)
   230  	if err != nil {
   231  		return prettyError(err)
   232  	}
   233  	PrintStatus(u.out, status)
   234  
   235  	return nil
   236  }
   237  
   238  func (u *upgradeCmd) vals() ([]byte, error) {
   239  	base := map[string]interface{}{}
   240  
   241  	// User specified a values files via -f/--values
   242  	for _, filePath := range u.valueFiles {
   243  		currentMap := map[string]interface{}{}
   244  		bytes, err := ioutil.ReadFile(filePath)
   245  		if err != nil {
   246  			return []byte{}, err
   247  		}
   248  
   249  		if err := yaml.Unmarshal(bytes, &currentMap); err != nil {
   250  			return []byte{}, fmt.Errorf("failed to parse %s: %s", filePath, err)
   251  		}
   252  		// Merge with the previous map
   253  		base = mergeValues(base, currentMap)
   254  	}
   255  
   256  	// User specified a value via --set
   257  	for _, value := range u.values {
   258  		if err := strvals.ParseInto(value, base); err != nil {
   259  			return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
   260  		}
   261  	}
   262  
   263  	return yaml.Marshal(base)
   264  }