github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/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 "context" 21 "fmt" 22 "io" 23 "log" 24 "os" 25 "os/signal" 26 "syscall" 27 "time" 28 29 "github.com/pkg/errors" 30 "github.com/spf13/cobra" 31 32 "github.com/stefanmcshane/helm/cmd/helm/require" 33 "github.com/stefanmcshane/helm/pkg/action" 34 "github.com/stefanmcshane/helm/pkg/chart/loader" 35 "github.com/stefanmcshane/helm/pkg/cli/output" 36 "github.com/stefanmcshane/helm/pkg/cli/values" 37 "github.com/stefanmcshane/helm/pkg/downloader" 38 "github.com/stefanmcshane/helm/pkg/getter" 39 "github.com/stefanmcshane/helm/pkg/storage/driver" 40 ) 41 42 const upgradeDesc = ` 43 This command upgrades a release to a new version of a chart. 44 45 The upgrade arguments must be a release and chart. The chart 46 argument can be either: a chart reference('example/mariadb'), a path to a chart directory, 47 a packaged chart, or a fully qualified URL. For chart references, the latest 48 version will be specified unless the '--version' flag is set. 49 50 To override values in a chart, use either the '--values' flag and pass in a file 51 or use the '--set' flag and pass configuration from the command line, to force string 52 values, use '--set-string'. You can use '--set-file' to set individual 53 values from a file when the value itself is too long for the command line 54 or is dynamically generated. You can also use '--set-json' to set json values 55 (scalars/objects/arrays) from the command line. 56 57 You can specify the '--values'/'-f' flag multiple times. The priority will be given to the 58 last (right-most) file specified. For example, if both myvalues.yaml and override.yaml 59 contained a key called 'Test', the value set in override.yaml would take precedence: 60 61 $ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis 62 63 You can specify the '--set' flag multiple times. The priority will be given to the 64 last (right-most) set specified. For example, if both 'bar' and 'newbar' values are 65 set for a key called 'foo', the 'newbar' value would take precedence: 66 67 $ helm upgrade --set foo=bar --set foo=newbar redis ./redis 68 ` 69 70 func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 71 client := action.NewUpgrade(cfg) 72 valueOpts := &values.Options{} 73 var outfmt output.Format 74 var createNamespace bool 75 76 cmd := &cobra.Command{ 77 Use: "upgrade [RELEASE] [CHART]", 78 Short: "upgrade a release", 79 Long: upgradeDesc, 80 Args: require.ExactArgs(2), 81 ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 82 if len(args) == 0 { 83 return compListReleases(toComplete, args, cfg) 84 } 85 if len(args) == 1 { 86 return compListCharts(toComplete, true) 87 } 88 return nil, cobra.ShellCompDirectiveNoFileComp 89 }, 90 RunE: func(cmd *cobra.Command, args []string) error { 91 client.Namespace = settings.Namespace() 92 93 // Fixes #7002 - Support reading values from STDIN for `upgrade` command 94 // Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice 95 if client.Install { 96 // If a release does not exist, install it. 97 histClient := action.NewHistory(cfg) 98 histClient.Max = 1 99 if _, err := histClient.Run(args[0]); err == driver.ErrReleaseNotFound { 100 // Only print this to stdout for table output 101 if outfmt == output.Table { 102 fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0]) 103 } 104 instClient := action.NewInstall(cfg) 105 instClient.CreateNamespace = createNamespace 106 instClient.ChartPathOptions = client.ChartPathOptions 107 instClient.DryRun = client.DryRun 108 instClient.DisableHooks = client.DisableHooks 109 instClient.SkipCRDs = client.SkipCRDs 110 instClient.Timeout = client.Timeout 111 instClient.Wait = client.Wait 112 instClient.WaitForJobs = client.WaitForJobs 113 instClient.Devel = client.Devel 114 instClient.Namespace = client.Namespace 115 instClient.Atomic = client.Atomic 116 instClient.PostRenderer = client.PostRenderer 117 instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation 118 instClient.SubNotes = client.SubNotes 119 instClient.Description = client.Description 120 instClient.DependencyUpdate = client.DependencyUpdate 121 122 rel, err := runInstall(args, instClient, valueOpts, out) 123 if err != nil { 124 return err 125 } 126 return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) 127 } else if err != nil { 128 return err 129 } 130 } 131 132 if client.Version == "" && client.Devel { 133 debug("setting version to >0.0.0-0") 134 client.Version = ">0.0.0-0" 135 } 136 137 chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings) 138 if err != nil { 139 return err 140 } 141 142 p := getter.All(settings) 143 vals, err := valueOpts.MergeValues(p) 144 if err != nil { 145 return err 146 } 147 148 // Check chart dependencies to make sure all are present in /charts 149 ch, err := loader.Load(chartPath) 150 if err != nil { 151 return err 152 } 153 if req := ch.Metadata.Dependencies; req != nil { 154 if err := action.CheckDependencies(ch, req); err != nil { 155 err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies") 156 if client.DependencyUpdate { 157 man := &downloader.Manager{ 158 Out: out, 159 ChartPath: chartPath, 160 Keyring: client.ChartPathOptions.Keyring, 161 SkipUpdate: false, 162 Getters: p, 163 RepositoryConfig: settings.RepositoryConfig, 164 RepositoryCache: settings.RepositoryCache, 165 Debug: settings.Debug, 166 } 167 if err := man.Update(); err != nil { 168 return err 169 } 170 // Reload the chart with the updated Chart.lock file. 171 if ch, err = loader.Load(chartPath); err != nil { 172 return errors.Wrap(err, "failed reloading chart after repo update") 173 } 174 } else { 175 return err 176 } 177 } 178 } 179 180 if ch.Metadata.Deprecated { 181 warning("This chart is deprecated") 182 } 183 184 // Create context and prepare the handle of SIGTERM 185 ctx := context.Background() 186 ctx, cancel := context.WithCancel(ctx) 187 188 // Set up channel on which to send signal notifications. 189 // We must use a buffered channel or risk missing the signal 190 // if we're not ready to receive when the signal is sent. 191 cSignal := make(chan os.Signal, 2) 192 signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) 193 go func() { 194 <-cSignal 195 fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0]) 196 cancel() 197 }() 198 199 rel, err := client.RunWithContext(ctx, args[0], ch, vals) 200 if err != nil { 201 return errors.Wrap(err, "UPGRADE FAILED") 202 } 203 204 if outfmt == output.Table { 205 fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0]) 206 } 207 208 return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) 209 }, 210 } 211 212 f := cmd.Flags() 213 f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present") 214 f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install") 215 f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") 216 f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade") 217 f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") 218 f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods") 219 f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy") 220 f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks") 221 f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema") 222 f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed when an upgrade is performed with install flag enabled. By default, CRDs are installed if not already present, when an upgrade is performed with install flag enabled") 223 f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") 224 f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") 225 f.BoolVar(&client.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") 226 f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") 227 f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout") 228 f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used") 229 f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit") 230 f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") 231 f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") 232 f.StringVar(&client.Description, "description", "", "add a custom description") 233 f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") 234 addChartPathOptionsFlags(f, &client.ChartPathOptions) 235 addValueOptionsFlags(f, valueOpts) 236 bindOutputFlag(cmd, &outfmt) 237 bindPostRenderFlag(cmd, &client.PostRenderer) 238 239 err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 240 if len(args) != 2 { 241 return nil, cobra.ShellCompDirectiveNoFileComp 242 } 243 return compVersionFlag(args[1], toComplete) 244 }) 245 246 if err != nil { 247 log.Fatal(err) 248 } 249 250 return cmd 251 }