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