github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/install.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  	"github.com/spf13/pflag"
    32  
    33  	"helm.sh/helm/v3/cmd/helm/require"
    34  	"helm.sh/helm/v3/pkg/action"
    35  	"helm.sh/helm/v3/pkg/chart"
    36  	"helm.sh/helm/v3/pkg/chart/loader"
    37  	"helm.sh/helm/v3/pkg/cli/output"
    38  	"helm.sh/helm/v3/pkg/cli/values"
    39  	"helm.sh/helm/v3/pkg/downloader"
    40  	"helm.sh/helm/v3/pkg/getter"
    41  	"helm.sh/helm/v3/pkg/release"
    42  )
    43  
    44  const installDesc = `
    45  This command installs a chart archive.
    46  
    47  The install argument must be a chart reference, a path to a packaged chart,
    48  a path to an unpacked chart directory or a URL.
    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
    52  a string value 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      $ helm install -f myvalues.yaml myredis ./redis
    58  
    59  or
    60  
    61      $ helm install --set name=prod myredis ./redis
    62  
    63  or
    64  
    65      $ helm install --set-string long_int=1234567890 myredis ./redis
    66  
    67  or
    68  
    69      $ helm install --set-file my_script=dothings.sh myredis ./redis
    70  
    71  or
    72  
    73      $ helm install --set-json 'master.sidecars=[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]' myredis ./redis
    74  
    75  
    76  You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
    77  last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
    78  contained a key called 'Test', the value set in override.yaml would take precedence:
    79  
    80      $ helm install -f myvalues.yaml -f override.yaml  myredis ./redis
    81  
    82  You can specify the '--set' flag multiple times. The priority will be given to the
    83  last (right-most) set specified. For example, if both 'bar' and 'newbar' values are
    84  set for a key called 'foo', the 'newbar' value would take precedence:
    85  
    86      $ helm install --set foo=bar --set foo=newbar  myredis ./redis
    87  
    88  Similarly, in the following example 'foo' is set to '["four"]': 
    89  
    90      $ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis
    91  
    92  And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}':
    93  
    94      $ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis
    95  
    96  To check the generated manifests of a release without installing the chart,
    97  the '--debug' and '--dry-run' flags can be combined.
    98  
    99  If --verify is set, the chart MUST have a provenance file, and the provenance
   100  file MUST pass all verification steps.
   101  
   102  There are six different ways you can express the chart you want to install:
   103  
   104  1. By chart reference: helm install mymaria example/mariadb
   105  2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz
   106  3. By path to an unpacked chart directory: helm install mynginx ./nginx
   107  4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz
   108  5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx
   109  6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx
   110  
   111  CHART REFERENCES
   112  
   113  A chart reference is a convenient way of referencing a chart in a chart repository.
   114  
   115  When you use a chart reference with a repo prefix ('example/mariadb'), Helm will look in the local
   116  configuration for a chart repository named 'example', and will then look for a
   117  chart in that repository whose name is 'mariadb'. It will install the latest stable version of that chart
   118  until you specify '--devel' flag to also include development version (alpha, beta, and release candidate releases), or
   119  supply a version number with the '--version' flag.
   120  
   121  To see the list of chart repositories, use 'helm repo list'. To search for
   122  charts in a repository, use 'helm search'.
   123  `
   124  
   125  func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
   126  	client := action.NewInstall(cfg)
   127  	valueOpts := &values.Options{}
   128  	var outfmt output.Format
   129  
   130  	cmd := &cobra.Command{
   131  		Use:   "install [NAME] [CHART]",
   132  		Short: "install a chart",
   133  		Long:  installDesc,
   134  		Args:  require.MinimumNArgs(1),
   135  		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   136  			return compInstall(args, toComplete, client)
   137  		},
   138  		RunE: func(_ *cobra.Command, args []string) error {
   139  			registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
   140  			if err != nil {
   141  				return fmt.Errorf("missing registry client: %w", err)
   142  			}
   143  			client.SetRegistryClient(registryClient)
   144  
   145  			rel, err := runInstall(args, client, valueOpts, out)
   146  			if err != nil {
   147  				return errors.Wrap(err, "INSTALLATION FAILED")
   148  			}
   149  
   150  			return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
   151  		},
   152  	}
   153  
   154  	addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
   155  	bindOutputFlag(cmd, &outfmt)
   156  	bindPostRenderFlag(cmd, &client.PostRenderer)
   157  
   158  	return cmd
   159  }
   160  
   161  func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
   162  	f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
   163  	f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
   164  	f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
   165  	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
   166  	f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
   167  	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
   168  	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")
   169  	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")
   170  	f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
   171  	f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
   172  	f.StringVar(&client.Description, "description", "", "add a custom description")
   173  	f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
   174  	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
   175  	f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
   176  	f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
   177  	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
   178  	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
   179  	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
   180  	addValueOptionsFlags(f, valueOpts)
   181  	addChartPathOptionsFlags(f, &client.ChartPathOptions)
   182  
   183  	err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   184  		requiredArgs := 2
   185  		if client.GenerateName {
   186  			requiredArgs = 1
   187  		}
   188  		if len(args) != requiredArgs {
   189  			return nil, cobra.ShellCompDirectiveNoFileComp
   190  		}
   191  		return compVersionFlag(args[requiredArgs-1], toComplete)
   192  	})
   193  
   194  	if err != nil {
   195  		log.Fatal(err)
   196  	}
   197  }
   198  
   199  func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) {
   200  	debug("Original chart version: %q", client.Version)
   201  	if client.Version == "" && client.Devel {
   202  		debug("setting version to >0.0.0-0")
   203  		client.Version = ">0.0.0-0"
   204  	}
   205  
   206  	name, chart, err := client.NameAndChart(args)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	client.ReleaseName = name
   211  
   212  	cp, err := client.ChartPathOptions.LocateChart(chart, settings)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	debug("CHART PATH: %s\n", cp)
   218  
   219  	p := getter.All(settings)
   220  	vals, err := valueOpts.MergeValues(p)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	// Check chart dependencies to make sure all are present in /charts
   226  	chartRequested, err := loader.Load(cp)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	if err := checkIfInstallable(chartRequested); err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	if chartRequested.Metadata.Deprecated {
   236  		warning("This chart is deprecated")
   237  	}
   238  
   239  	if req := chartRequested.Metadata.Dependencies; req != nil {
   240  		// If CheckDependencies returns an error, we have unfulfilled dependencies.
   241  		// As of Helm 2.4.0, this is treated as a stopping condition:
   242  		// https://github.com/helm/helm/issues/2209
   243  		if err := action.CheckDependencies(chartRequested, req); err != nil {
   244  			err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies")
   245  			if client.DependencyUpdate {
   246  				man := &downloader.Manager{
   247  					Out:              out,
   248  					ChartPath:        cp,
   249  					Keyring:          client.ChartPathOptions.Keyring,
   250  					SkipUpdate:       false,
   251  					Getters:          p,
   252  					RepositoryConfig: settings.RepositoryConfig,
   253  					RepositoryCache:  settings.RepositoryCache,
   254  					Debug:            settings.Debug,
   255  				}
   256  				if err := man.Update(); err != nil {
   257  					return nil, err
   258  				}
   259  				// Reload the chart with the updated Chart.lock file.
   260  				if chartRequested, err = loader.Load(cp); err != nil {
   261  					return nil, errors.Wrap(err, "failed reloading chart after repo update")
   262  				}
   263  			} else {
   264  				return nil, err
   265  			}
   266  		}
   267  	}
   268  
   269  	client.Namespace = settings.Namespace()
   270  
   271  	// Create context and prepare the handle of SIGTERM
   272  	ctx := context.Background()
   273  	ctx, cancel := context.WithCancel(ctx)
   274  
   275  	// Set up channel on which to send signal notifications.
   276  	// We must use a buffered channel or risk missing the signal
   277  	// if we're not ready to receive when the signal is sent.
   278  	cSignal := make(chan os.Signal, 2)
   279  	signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
   280  	go func() {
   281  		<-cSignal
   282  		fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0])
   283  		cancel()
   284  	}()
   285  
   286  	return client.RunWithContext(ctx, chartRequested, vals)
   287  }
   288  
   289  // checkIfInstallable validates if a chart can be installed
   290  //
   291  // Application chart type is only installable
   292  func checkIfInstallable(ch *chart.Chart) error {
   293  	switch ch.Metadata.Type {
   294  	case "", "application":
   295  		return nil
   296  	}
   297  	return errors.Errorf("%s charts are not installable", ch.Metadata.Type)
   298  }
   299  
   300  // Provide dynamic auto-completion for the install and template commands
   301  func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) {
   302  	requiredArgs := 1
   303  	if client.GenerateName {
   304  		requiredArgs = 0
   305  	}
   306  	if len(args) == requiredArgs {
   307  		return compListCharts(toComplete, true)
   308  	}
   309  	return nil, cobra.ShellCompDirectiveNoFileComp
   310  }