github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/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 "os" 23 "strings" 24 25 "github.com/spf13/cobra" 26 27 "k8s.io/helm/pkg/chartutil" 28 "k8s.io/helm/pkg/helm" 29 "k8s.io/helm/pkg/renderutil" 30 storageerrors "k8s.io/helm/pkg/storage/errors" 31 ) 32 33 const upgradeDesc = ` 34 This command upgrades a release to a specified version of a chart and/or updates chart values. 35 36 Required arguments are release and chart. The chart argument can be one of: 37 - a chart reference('stable/mariadb'); use '--version' and '--devel' flags for versions other than latest, 38 - a path to a chart directory, 39 - a packaged chart, 40 - a fully qualified URL. 41 42 To customize the chart values, use any of 43 - '--values'/'-f' to pass in a yaml file holding settings, 44 - '--set' to provide one or more key=val pairs directly, 45 - '--set-string' to provide key=val forcing val to be stored as a string, 46 - '--set-file' to provide key=path to read a single large value from a file at path. 47 48 To edit or append to the existing customized values, add the 49 '--reuse-values' flag, otherwise any existing customized values are ignored. 50 51 If no chart value arguments are provided on the command line, any existing customized values are carried 52 forward. If you want to revert to just the values provided in the chart, use the '--reset-values' flag. 53 54 You can specify any of the chart value flags multiple times. The priority will be given to the last 55 (right-most) value specified. For example, if both myvalues.yaml and override.yaml contained a key 56 called 'Test', the value set in override.yaml would take precedence: 57 58 $ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis 59 60 Note that the key name provided to the '--set', '--set-string' and '--set-file' flags can reference 61 structure elements. Examples: 62 - mybool=TRUE 63 - livenessProbe.timeoutSeconds=10 64 - metrics.annotations[0]=hey,metrics.annotations[1]=ho 65 66 which sets the top level key mybool to true, the nested timeoutSeconds to 10, and two array values, respectively. 67 68 Note that the value side of the key=val provided to '--set' and '--set-string' flags will pass through 69 shell evaluation followed by yaml type parsing to produce the final value. This may alter inputs with 70 special characters in unexpected ways, for example 71 72 $ helm upgrade --set pwd=3jk$o2,z=f\30.e redis ./redis 73 74 results in "pwd: 3jk" and "z: f30.e". Use single quotes to avoid shell evaluation and argument delimiters, 75 and use backslash to escape yaml special characters: 76 77 $ helm upgrade --set pwd='3jk$o2z=f\\30.e' redis ./redis 78 79 which results in the expected "pwd: 3jk$o2z=f\30.e". If a single quote occurs in your value then follow 80 your shell convention for escaping it; for example in bash: 81 82 $ helm upgrade --set pwd='3jk$o2z=f\\30with'\''quote' 83 84 which results in "pwd: 3jk$o2z=f\30with'quote". 85 ` 86 87 type upgradeCmd struct { 88 release string 89 chart string 90 out io.Writer 91 client helm.Interface 92 dryRun bool 93 recreate bool 94 force bool 95 disableHooks bool 96 valueFiles valueFiles 97 values []string 98 stringValues []string 99 fileValues []string 100 verify bool 101 keyring string 102 install bool 103 namespace string 104 version string 105 timeout int64 106 resetValues bool 107 reuseValues bool 108 wait bool 109 atomic bool 110 repoURL string 111 username string 112 password string 113 devel bool 114 subNotes bool 115 description string 116 cleanupOnFail bool 117 118 certFile string 119 keyFile string 120 caFile string 121 output string 122 } 123 124 func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { 125 126 upgrade := &upgradeCmd{ 127 out: out, 128 client: client, 129 } 130 131 cmd := &cobra.Command{ 132 Use: "upgrade [RELEASE] [CHART]", 133 Short: "Upgrade a release", 134 Long: upgradeDesc, 135 PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() }, 136 RunE: func(cmd *cobra.Command, args []string) error { 137 if err := checkArgsLength(len(args), "release name", "chart path"); err != nil { 138 return err 139 } 140 141 if upgrade.version == "" && upgrade.devel { 142 debug("setting version to >0.0.0-0") 143 upgrade.version = ">0.0.0-0" 144 } 145 146 upgrade.release = args[0] 147 upgrade.chart = args[1] 148 upgrade.client = ensureHelmClient(upgrade.client) 149 upgrade.wait = upgrade.wait || upgrade.atomic 150 151 return upgrade.run() 152 }, 153 } 154 155 f := cmd.Flags() 156 settings.AddFlagsTLS(f) 157 f.VarP(&upgrade.valueFiles, "values", "f", "Specify values in a YAML file or a URL(can specify multiple)") 158 f.BoolVar(&upgrade.dryRun, "dry-run", false, "Simulate an upgrade") 159 f.BoolVar(&upgrade.recreate, "recreate-pods", false, "Performs pods restart for the resource if applicable") 160 f.BoolVar(&upgrade.force, "force", false, "Force resource update through delete/recreate if needed") 161 f.StringArrayVar(&upgrade.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") 162 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)") 163 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)") 164 f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "Disable pre/post upgrade hooks. DEPRECATED. Use no-hooks") 165 f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "Disable pre/post upgrade hooks") 166 f.BoolVar(&upgrade.verify, "verify", false, "Verify the provenance of the chart before upgrading") 167 f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "Path to the keyring that contains public signing keys") 168 f.BoolVarP(&upgrade.install, "install", "i", false, "If a release by this name doesn't already exist, run an install") 169 f.StringVar(&upgrade.namespace, "namespace", "", "Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace") 170 f.StringVar(&upgrade.version, "version", "", "Specify the exact chart version to use. If this is not specified, the latest version is used") 171 f.Int64Var(&upgrade.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") 172 f.BoolVar(&upgrade.resetValues, "reset-values", false, "When upgrading, reset the values to the ones built into the chart") 173 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.") 174 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") 175 f.BoolVar(&upgrade.atomic, "atomic", false, "If set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag") 176 f.StringVar(&upgrade.repoURL, "repo", "", "Chart repository url where to locate the requested chart") 177 f.StringVar(&upgrade.username, "username", "", "Chart repository username where to locate the requested chart") 178 f.StringVar(&upgrade.password, "password", "", "Chart repository password where to locate the requested chart") 179 f.StringVar(&upgrade.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file") 180 f.StringVar(&upgrade.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file") 181 f.StringVar(&upgrade.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle") 182 f.BoolVar(&upgrade.devel, "devel", false, "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") 183 f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent") 184 f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default") 185 f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed") 186 bindOutputFlag(cmd, &upgrade.output) 187 188 f.MarkDeprecated("disable-hooks", "Use --no-hooks instead") 189 190 // set defaults from environment 191 settings.InitTLS(f) 192 193 return cmd 194 } 195 196 func (u *upgradeCmd) run() error { 197 chartPath, err := locateChartPath(u.repoURL, u.username, u.password, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) 198 if err != nil { 199 return err 200 } 201 202 releaseHistory, err := u.client.ReleaseHistory(u.release, helm.WithMaxHistory(1)) 203 204 if u.install { 205 // If a release does not exist, install it. If another error occurs during 206 // the check, ignore the error and continue with the upgrade. 207 // 208 // The returned error is a grpc.rpcError that wraps the message from the original error. 209 // So we're stuck doing string matching against the wrapped error, which is nested somewhere 210 // inside of the grpc.rpcError message. 211 212 if err == nil { 213 if u.namespace == "" { 214 u.namespace = defaultNamespace() 215 } 216 previousReleaseNamespace := releaseHistory.Releases[0].Namespace 217 if previousReleaseNamespace != u.namespace { 218 fmt.Fprintf(u.out, 219 "WARNING: Namespace %q doesn't match with previous. Release will be deployed to %s\n", 220 u.namespace, previousReleaseNamespace, 221 ) 222 } 223 } 224 225 if err != nil && strings.Contains(err.Error(), storageerrors.ErrReleaseNotFound(u.release).Error()) { 226 fmt.Fprintf(u.out, "Release %q does not exist. Installing it now.\n", u.release) 227 ic := &installCmd{ 228 chartPath: chartPath, 229 client: u.client, 230 out: u.out, 231 name: u.release, 232 valueFiles: u.valueFiles, 233 dryRun: u.dryRun, 234 verify: u.verify, 235 disableHooks: u.disableHooks, 236 keyring: u.keyring, 237 values: u.values, 238 stringValues: u.stringValues, 239 fileValues: u.fileValues, 240 namespace: u.namespace, 241 timeout: u.timeout, 242 wait: u.wait, 243 description: u.description, 244 atomic: u.atomic, 245 output: u.output, 246 } 247 return ic.run() 248 } 249 } 250 251 rawVals, err := vals(u.valueFiles, u.values, u.stringValues, u.fileValues, u.certFile, u.keyFile, u.caFile) 252 if err != nil { 253 return err 254 } 255 256 // Check chart requirements to make sure all dependencies are present in /charts 257 ch, err := chartutil.Load(chartPath) 258 if err == nil { 259 if req, err := chartutil.LoadRequirements(ch); err == nil { 260 if err := renderutil.CheckDependencies(ch, req); err != nil { 261 return err 262 } 263 } else if err != chartutil.ErrRequirementsNotFound { 264 return fmt.Errorf("cannot load requirements: %v", err) 265 } 266 } else { 267 return prettyError(err) 268 } 269 270 if ch.Metadata.Deprecated { 271 fmt.Fprintln(os.Stderr, "WARNING: This chart is deprecated") 272 } 273 274 resp, err := u.client.UpdateReleaseFromChart( 275 u.release, 276 ch, 277 helm.UpdateValueOverrides(rawVals), 278 helm.UpgradeDryRun(u.dryRun), 279 helm.UpgradeRecreate(u.recreate), 280 helm.UpgradeForce(u.force), 281 helm.UpgradeDisableHooks(u.disableHooks), 282 helm.UpgradeTimeout(u.timeout), 283 helm.ResetValues(u.resetValues), 284 helm.ReuseValues(u.reuseValues), 285 helm.UpgradeSubNotes(u.subNotes), 286 helm.UpgradeWait(u.wait), 287 helm.UpgradeDescription(u.description), 288 helm.UpgradeCleanupOnFail(u.cleanupOnFail)) 289 if err != nil { 290 fmt.Fprintf(u.out, "UPGRADE FAILED\nError: %v\n", prettyError(err)) 291 if u.atomic { 292 fmt.Fprint(u.out, "ROLLING BACK") 293 rollback := &rollbackCmd{ 294 out: u.out, 295 client: u.client, 296 name: u.release, 297 dryRun: u.dryRun, 298 recreate: u.recreate, 299 force: u.force, 300 timeout: u.timeout, 301 wait: u.wait, 302 description: "", 303 revision: releaseHistory.Releases[0].Version, 304 disableHooks: u.disableHooks, 305 cleanupOnFail: u.cleanupOnFail, 306 } 307 if err := rollback.run(); err != nil { 308 return err 309 } 310 } 311 return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) 312 } 313 314 if settings.Debug { 315 printRelease(u.out, resp.Release) 316 } 317 318 if outputFormat(u.output) == outputTable { 319 fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release) 320 } 321 // Print the status like status command does 322 status, err := u.client.ReleaseStatus(u.release) 323 if err != nil { 324 return prettyError(err) 325 } 326 327 return write(u.out, &statusWriter{status}, outputFormat(u.output)) 328 }