github.com/latiif/helm@v2.15.0+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 atomic bool 109 repoURL string 110 username string 111 password string 112 devel bool 113 subNotes bool 114 description string 115 cleanupOnFail bool 116 117 certFile string 118 keyFile string 119 caFile string 120 output string 121 } 122 123 func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { 124 125 upgrade := &upgradeCmd{ 126 out: out, 127 client: client, 128 } 129 130 cmd := &cobra.Command{ 131 Use: "upgrade [RELEASE] [CHART]", 132 Short: "Upgrade a release", 133 Long: upgradeDesc, 134 PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() }, 135 RunE: func(cmd *cobra.Command, args []string) error { 136 if err := checkArgsLength(len(args), "release name", "chart path"); err != nil { 137 return err 138 } 139 140 if upgrade.version == "" && upgrade.devel { 141 debug("setting version to >0.0.0-0") 142 upgrade.version = ">0.0.0-0" 143 } 144 145 upgrade.release = args[0] 146 upgrade.chart = args[1] 147 upgrade.client = ensureHelmClient(upgrade.client) 148 upgrade.wait = upgrade.wait || upgrade.atomic 149 150 return upgrade.run() 151 }, 152 } 153 154 f := cmd.Flags() 155 settings.AddFlagsTLS(f) 156 f.VarP(&upgrade.valueFiles, "values", "f", "Specify values in a YAML file or a URL(can specify multiple)") 157 f.BoolVar(&upgrade.dryRun, "dry-run", false, "Simulate an upgrade") 158 f.BoolVar(&upgrade.recreate, "recreate-pods", false, "Performs pods restart for the resource if applicable") 159 f.BoolVar(&upgrade.force, "force", false, "Force resource update through delete/recreate if needed") 160 f.StringArrayVar(&upgrade.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") 161 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)") 162 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)") 163 f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "Disable pre/post upgrade hooks. DEPRECATED. Use no-hooks") 164 f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "Disable pre/post upgrade hooks") 165 f.BoolVar(&upgrade.verify, "verify", false, "Verify the provenance of the chart before upgrading") 166 f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "Path to the keyring that contains public signing keys") 167 f.BoolVarP(&upgrade.install, "install", "i", false, "If a release by this name doesn't already exist, run an install") 168 f.StringVar(&upgrade.namespace, "namespace", "", "Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace") 169 f.StringVar(&upgrade.version, "version", "", "Specify the exact chart version to use. If this is not specified, the latest version is used") 170 f.Int64Var(&upgrade.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") 171 f.BoolVar(&upgrade.resetValues, "reset-values", false, "When upgrading, reset the values to the ones built into the chart") 172 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.") 173 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") 174 f.BoolVar(&upgrade.atomic, "atomic", false, "If set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag") 175 f.StringVar(&upgrade.repoURL, "repo", "", "Chart repository url where to locate the requested chart") 176 f.StringVar(&upgrade.username, "username", "", "Chart repository username where to locate the requested chart") 177 f.StringVar(&upgrade.password, "password", "", "Chart repository password where to locate the requested chart") 178 f.StringVar(&upgrade.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file") 179 f.StringVar(&upgrade.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file") 180 f.StringVar(&upgrade.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle") 181 f.BoolVar(&upgrade.devel, "devel", false, "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") 182 f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent") 183 f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default") 184 f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed") 185 bindOutputFlag(cmd, &upgrade.output) 186 187 f.MarkDeprecated("disable-hooks", "Use --no-hooks instead") 188 189 // set defaults from environment 190 settings.InitTLS(f) 191 192 return cmd 193 } 194 195 func (u *upgradeCmd) run() error { 196 chartPath, err := locateChartPath(u.repoURL, u.username, u.password, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) 197 if err != nil { 198 return err 199 } 200 201 releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1)) 202 203 if u.install { 204 // If a release does not exist, install it. If another error occurs during 205 // the check, ignore the error and continue with the upgrade. 206 // 207 // The returned error is a grpc.rpcError that wraps the message from the original error. 208 // So we're stuck doing string matching against the wrapped error, which is nested somewhere 209 // inside of the grpc.rpcError message. 210 211 if err == nil { 212 if u.namespace == "" { 213 u.namespace = defaultNamespace() 214 } 215 previousReleaseNamespace := releaseHistory.Releases[0].Namespace 216 if previousReleaseNamespace != u.namespace { 217 fmt.Fprintf(u.out, 218 "WARNING: Namespace %q doesn't match with previous. Release will be deployed to %s\n", 219 u.namespace, previousReleaseNamespace, 220 ) 221 } 222 } 223 224 if err != nil && strings.Contains(err.Error(), storageerrors.ErrReleaseNotFound(u.release).Error()) { 225 fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release) 226 ic := &installCmd{ 227 chartPath: chartPath, 228 client: u.client, 229 out: u.out, 230 name: u.release, 231 valueFiles: u.valueFiles, 232 dryRun: u.dryRun, 233 verify: u.verify, 234 disableHooks: u.disableHooks, 235 keyring: u.keyring, 236 values: u.values, 237 stringValues: u.stringValues, 238 fileValues: u.fileValues, 239 namespace: u.namespace, 240 timeout: u.timeout, 241 wait: u.wait, 242 description: u.description, 243 atomic: u.atomic, 244 } 245 return ic.run() 246 } 247 } 248 249 rawVals, err := vals(u.valueFiles, u.values, u.stringValues, u.fileValues, u.certFile, u.keyFile, u.caFile) 250 if err != nil { 251 return err 252 } 253 254 // Check chart requirements to make sure all dependencies are present in /charts 255 ch, err := chartutil.Load(chartPath) 256 if err == nil { 257 if req, err := chartutil.LoadRequirements(ch); err == nil { 258 if err := renderutil.CheckDependencies(ch, req); err != nil { 259 return err 260 } 261 } else if err != chartutil.ErrRequirementsNotFound { 262 return fmt.Errorf("cannot load requirements: %v", err) 263 } 264 } else { 265 return prettyError(err) 266 } 267 268 resp, err := u.client.UpdateReleaseFromChart( 269 u.release, 270 ch, 271 helm.UpdateValueOverrides(rawVals), 272 helm.UpgradeDryRun(u.dryRun), 273 helm.UpgradeRecreate(u.recreate), 274 helm.UpgradeForce(u.force), 275 helm.UpgradeDisableHooks(u.disableHooks), 276 helm.UpgradeTimeout(u.timeout), 277 helm.ResetValues(u.resetValues), 278 helm.ReuseValues(u.reuseValues), 279 helm.UpgradeSubNotes(u.subNotes), 280 helm.UpgradeWait(u.wait), 281 helm.UpgradeDescription(u.description), 282 helm.UpgradeCleanupOnFail(u.cleanupOnFail)) 283 if err != nil { 284 fmt.Fprintf(u.out, "UPGRADE FAILED\nError: %v\n", prettyError(err)) 285 if u.atomic { 286 fmt.Fprint(u.out, "ROLLING BACK") 287 rollback := &rollbackCmd{ 288 out: u.out, 289 client: u.client, 290 name: u.release, 291 dryRun: u.dryRun, 292 recreate: u.recreate, 293 force: u.force, 294 timeout: u.timeout, 295 wait: u.wait, 296 description: "", 297 revision: releaseHistory.Releases[0].Version, 298 disableHooks: u.disableHooks, 299 cleanupOnFail: u.cleanupOnFail, 300 } 301 if err := rollback.run(); err != nil { 302 return err 303 } 304 } 305 return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) 306 } 307 308 if settings.Debug { 309 printRelease(u.out, resp.Release) 310 } 311 312 if outputFormat(u.output) == outputTable { 313 fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release) 314 } 315 // Print the status like status command does 316 status, err := u.client.ReleaseStatus(u.release) 317 if err != nil { 318 return prettyError(err) 319 } 320 321 return write(u.out, &statusWriter{status}, outputFormat(u.output)) 322 }