github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/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 cmd
    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  	"helm.sh/helm/v3/cmd/helm/require"
    33  	"helm.sh/helm/v3/pkg/action"
    34  	"helm.sh/helm/v3/pkg/chart/loader"
    35  	"helm.sh/helm/v3/pkg/cli/output"
    36  	"helm.sh/helm/v3/pkg/cli/values"
    37  	"helm.sh/helm/v3/pkg/downloader"
    38  	"helm.sh/helm/v3/pkg/getter"
    39  	"helm.sh/helm/v3/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  			registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
    94  			if err != nil {
    95  				return fmt.Errorf("missing registry client: %w", err)
    96  			}
    97  			client.SetRegistryClient(registryClient)
    98  
    99  			// Fixes #7002 - Support reading values from STDIN for `upgrade` command
   100  			// Must load values AFTER determining if we have to call install so that values loaded from stdin are are not read twice
   101  			if client.Install {
   102  				// If a release does not exist, install it.
   103  				histClient := action.NewHistory(cfg)
   104  				histClient.Max = 1
   105  				if _, err := histClient.Run(args[0]); err == driver.ErrReleaseNotFound {
   106  					// Only print this to stdout for table output
   107  					if outfmt == output.Table {
   108  						fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0])
   109  					}
   110  					instClient := action.NewInstall(cfg)
   111  					instClient.CreateNamespace = createNamespace
   112  					instClient.ChartPathOptions = client.ChartPathOptions
   113  					instClient.Force = client.Force
   114  					instClient.DryRun = client.DryRun
   115  					instClient.DisableHooks = client.DisableHooks
   116  					instClient.SkipCRDs = client.SkipCRDs
   117  					instClient.Timeout = client.Timeout
   118  					instClient.Wait = client.Wait
   119  					instClient.WaitForJobs = client.WaitForJobs
   120  					instClient.Devel = client.Devel
   121  					instClient.Namespace = client.Namespace
   122  					instClient.Atomic = client.Atomic
   123  					instClient.PostRenderer = client.PostRenderer
   124  					instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
   125  					instClient.SubNotes = client.SubNotes
   126  					instClient.Description = client.Description
   127  					instClient.DependencyUpdate = client.DependencyUpdate
   128  					instClient.EnableDNS = client.EnableDNS
   129  
   130  					rel, err := runInstall(args, instClient, valueOpts, out)
   131  					if err != nil {
   132  						return err
   133  					}
   134  					return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
   135  				} else if err != nil {
   136  					return err
   137  				}
   138  			}
   139  
   140  			if client.Version == "" && client.Devel {
   141  				debug("setting version to >0.0.0-0")
   142  				client.Version = ">0.0.0-0"
   143  			}
   144  
   145  			chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings)
   146  			if err != nil {
   147  				return err
   148  			}
   149  
   150  			p := getter.All(settings)
   151  			vals, err := valueOpts.MergeValues(p)
   152  			if err != nil {
   153  				return err
   154  			}
   155  
   156  			// Check chart dependencies to make sure all are present in /charts
   157  			ch, err := loader.Load(chartPath)
   158  			if err != nil {
   159  				return err
   160  			}
   161  			if req := ch.Metadata.Dependencies; req != nil {
   162  				if err := action.CheckDependencies(ch, req); err != nil {
   163  					err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
   164  					if client.DependencyUpdate {
   165  						man := &downloader.Manager{
   166  							Out:              out,
   167  							ChartPath:        chartPath,
   168  							Keyring:          client.ChartPathOptions.Keyring,
   169  							SkipUpdate:       false,
   170  							Getters:          p,
   171  							RepositoryConfig: settings.RepositoryConfig,
   172  							RepositoryCache:  settings.RepositoryCache,
   173  							Debug:            settings.Debug,
   174  						}
   175  						if err := man.Update(); err != nil {
   176  							return err
   177  						}
   178  						// Reload the chart with the updated Chart.lock file.
   179  						if ch, err = loader.Load(chartPath); err != nil {
   180  							return errors.Wrap(err, "failed reloading chart after repo update")
   181  						}
   182  					} else {
   183  						return err
   184  					}
   185  				}
   186  			}
   187  
   188  			if ch.Metadata.Deprecated {
   189  				warning("This chart is deprecated")
   190  			}
   191  
   192  			// Create context and prepare the handle of SIGTERM
   193  			ctx := context.Background()
   194  			ctx, cancel := context.WithCancel(ctx)
   195  
   196  			// Set up channel on which to send signal notifications.
   197  			// We must use a buffered channel or risk missing the signal
   198  			// if we're not ready to receive when the signal is sent.
   199  			cSignal := make(chan os.Signal, 2)
   200  			signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
   201  			go func() {
   202  				<-cSignal
   203  				fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
   204  				cancel()
   205  			}()
   206  
   207  			rel, err := client.RunWithContext(ctx, args[0], ch, vals)
   208  			if err != nil {
   209  				return errors.Wrap(err, "UPGRADE FAILED")
   210  			}
   211  
   212  			if outfmt == output.Table {
   213  				fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0])
   214  			}
   215  
   216  			return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
   217  		},
   218  	}
   219  
   220  	f := cmd.Flags()
   221  	f.BoolVar(&createNamespace, "create-namespace", false, "if --install is set, create the release namespace if not present")
   222  	f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
   223  	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
   224  	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade")
   225  	f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
   226  	f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods")
   227  	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
   228  	f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
   229  	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the upgrade process will not validate rendered templates against the Kubernetes OpenAPI Schema")
   230  	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")
   231  	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
   232  	f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
   233  	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")
   234  	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")
   235  	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")
   236  	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")
   237  	f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
   238  	f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
   239  	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
   240  	f.StringVar(&client.Description, "description", "", "add a custom description")
   241  	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
   242  	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
   243  	addChartPathOptionsFlags(f, &client.ChartPathOptions)
   244  	addValueOptionsFlags(f, valueOpts)
   245  	bindOutputFlag(cmd, &outfmt)
   246  	bindPostRenderFlag(cmd, &client.PostRenderer)
   247  
   248  	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   249  		if len(args) != 2 {
   250  			return nil, cobra.ShellCompDirectiveNoFileComp
   251  		}
   252  		return compVersionFlag(args[1], toComplete)
   253  	})
   254  
   255  	if err != nil {
   256  		log.Fatal(err)
   257  	}
   258  
   259  	return cmd
   260  }