github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+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  	disableHooks bool
    66  	valueFiles   valueFiles
    67  	values       []string
    68  	verify       bool
    69  	keyring      string
    70  	install      bool
    71  	namespace    string
    72  	version      string
    73  	timeout      int64
    74  	resetValues  bool
    75  	reuseValues  bool
    76  	wait         bool
    77  }
    78  
    79  func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
    80  
    81  	upgrade := &upgradeCmd{
    82  		out:    out,
    83  		client: client,
    84  	}
    85  
    86  	cmd := &cobra.Command{
    87  		Use:               "upgrade [RELEASE] [CHART]",
    88  		Short:             "upgrade a release",
    89  		Long:              upgradeDesc,
    90  		PersistentPreRunE: setupConnection,
    91  		RunE: func(cmd *cobra.Command, args []string) error {
    92  			if err := checkArgsLength(len(args), "release name", "chart path"); err != nil {
    93  				return err
    94  			}
    95  
    96  			upgrade.release = args[0]
    97  			upgrade.chart = args[1]
    98  			upgrade.client = ensureHelmClient(upgrade.client)
    99  
   100  			return upgrade.run()
   101  		},
   102  	}
   103  
   104  	f := cmd.Flags()
   105  	f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
   106  	f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
   107  	f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
   108  	f.StringArrayVar(&upgrade.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   109  	f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
   110  	f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
   111  	f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
   112  	f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys")
   113  	f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
   114  	f.StringVar(&upgrade.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)")
   115  	f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
   116  	f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)")
   117  	f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
   118  	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.")
   119  	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")
   120  
   121  	f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
   122  
   123  	return cmd
   124  }
   125  
   126  func (u *upgradeCmd) run() error {
   127  	chartPath, err := locateChartPath(u.chart, u.version, u.verify, u.keyring)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	if u.install {
   133  		// If a release does not exist, install it. If another error occurs during
   134  		// the check, ignore the error and continue with the upgrade.
   135  		//
   136  		// The returned error is a grpc.rpcError that wraps the message from the original error.
   137  		// So we're stuck doing string matching against the wrapped error, which is nested somewhere
   138  		// inside of the grpc.rpcError message.
   139  		_, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1))
   140  		if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound.Error()) {
   141  			fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release)
   142  			ic := &installCmd{
   143  				chartPath:    chartPath,
   144  				client:       u.client,
   145  				out:          u.out,
   146  				name:         u.release,
   147  				valueFiles:   u.valueFiles,
   148  				dryRun:       u.dryRun,
   149  				verify:       u.verify,
   150  				disableHooks: u.disableHooks,
   151  				keyring:      u.keyring,
   152  				values:       u.values,
   153  				namespace:    u.namespace,
   154  				timeout:      u.timeout,
   155  				wait:         u.wait,
   156  			}
   157  			return ic.run()
   158  		}
   159  	}
   160  
   161  	rawVals, err := u.vals()
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	// Check chart requirements to make sure all dependencies are present in /charts
   167  	if ch, err := chartutil.Load(chartPath); err == nil {
   168  		if req, err := chartutil.LoadRequirements(ch); err == nil {
   169  			checkDependencies(ch, req, u.out)
   170  		}
   171  	}
   172  
   173  	resp, err := u.client.UpdateRelease(
   174  		u.release,
   175  		chartPath,
   176  		helm.UpdateValueOverrides(rawVals),
   177  		helm.UpgradeDryRun(u.dryRun),
   178  		helm.UpgradeRecreate(u.recreate),
   179  		helm.UpgradeDisableHooks(u.disableHooks),
   180  		helm.UpgradeTimeout(u.timeout),
   181  		helm.ResetValues(u.resetValues),
   182  		helm.ReuseValues(u.reuseValues),
   183  		helm.UpgradeWait(u.wait))
   184  	if err != nil {
   185  		return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
   186  	}
   187  
   188  	if flagDebug {
   189  		printRelease(u.out, resp.Release)
   190  	}
   191  
   192  	fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)
   193  
   194  	// Print the status like status command does
   195  	status, err := u.client.ReleaseStatus(u.release)
   196  	if err != nil {
   197  		return prettyError(err)
   198  	}
   199  	PrintStatus(u.out, status)
   200  
   201  	return nil
   202  }
   203  
   204  func (u *upgradeCmd) vals() ([]byte, error) {
   205  	base := map[string]interface{}{}
   206  
   207  	// User specified a values files via -f/--values
   208  	for _, filePath := range u.valueFiles {
   209  		currentMap := map[string]interface{}{}
   210  		bytes, err := ioutil.ReadFile(filePath)
   211  		if err != nil {
   212  			return []byte{}, err
   213  		}
   214  
   215  		if err := yaml.Unmarshal(bytes, &currentMap); err != nil {
   216  			return []byte{}, fmt.Errorf("failed to parse %s: %s", filePath, err)
   217  		}
   218  		// Merge with the previous map
   219  		base = mergeValues(base, currentMap)
   220  	}
   221  
   222  	// User specified a value via --set
   223  	for _, value := range u.values {
   224  		if err := strvals.ParseInto(value, base); err != nil {
   225  			return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
   226  		}
   227  	}
   228  
   229  	return yaml.Marshal(base)
   230  }