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, ¤tMap); 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 }