github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/cmd/helm/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 main
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"net/url"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  	"text/template"
    30  
    31  	"github.com/Masterminds/sprig"
    32  	"github.com/ghodss/yaml"
    33  	"github.com/spf13/cobra"
    34  
    35  	"k8s.io/apimachinery/pkg/util/validation"
    36  	"k8s.io/helm/pkg/chartutil"
    37  	"k8s.io/helm/pkg/downloader"
    38  	"k8s.io/helm/pkg/getter"
    39  	"k8s.io/helm/pkg/helm"
    40  	"k8s.io/helm/pkg/kube"
    41  	"k8s.io/helm/pkg/proto/hapi/release"
    42  	"k8s.io/helm/pkg/renderutil"
    43  	"k8s.io/helm/pkg/repo"
    44  	"k8s.io/helm/pkg/strvals"
    45  )
    46  
    47  const installDesc = `
    48  This command installs a chart archive.
    49  
    50  The install argument must be a chart reference, a path to a packaged chart,
    51  a path to an unpacked chart directory or a URL.
    52  
    53  To override values in a chart, use either the '--values' flag and pass in a file
    54  or use the '--set' flag and pass configuration from the command line.  To force string
    55  values in '--set', use '--set-string' instead. In case a value is large and therefore
    56  you want not to use neither '--values' nor '--set', use '--set-file' to read the
    57  single large value from file.
    58  
    59  	$ helm install -f myvalues.yaml ./redis
    60  
    61  or
    62  
    63  	$ helm install --set name=prod ./redis
    64  
    65  or
    66  
    67  	$ helm install --set-string long_int=1234567890 ./redis
    68  
    69  or
    70      $ helm install --set-file multiline_text=path/to/textfile
    71  
    72  You can specify the '--values'/'-f' flag multiple times. The priority will be given to the
    73  last (right-most) file specified. For example, if both myvalues.yaml and override.yaml
    74  contained a key called 'Test', the value set in override.yaml would take precedence:
    75  
    76  	$ helm install -f myvalues.yaml -f override.yaml ./redis
    77  
    78  You can specify the '--set' flag multiple times. The priority will be given to the
    79  last (right-most) set specified. For example, if both 'bar' and 'newbar' values are
    80  set for a key called 'foo', the 'newbar' value would take precedence:
    81  
    82  	$ helm install --set foo=bar --set foo=newbar ./redis
    83  
    84  
    85  To check the generated manifests of a release without installing the chart,
    86  the '--debug' and '--dry-run' flags can be combined. This will still require a
    87  round-trip to the Tiller server.
    88  
    89  If --verify is set, the chart MUST have a provenance file, and the provenance
    90  file MUST pass all verification steps.
    91  
    92  There are five different ways you can express the chart you want to install:
    93  
    94  1. By chart reference: helm install stable/mariadb
    95  2. By path to a packaged chart: helm install ./nginx-1.2.3.tgz
    96  3. By path to an unpacked chart directory: helm install ./nginx
    97  4. By absolute URL: helm install https://example.com/charts/nginx-1.2.3.tgz
    98  5. By chart reference and repo url: helm install --repo https://example.com/charts/ nginx
    99  
   100  CHART REFERENCES
   101  
   102  A chart reference is a convenient way of reference a chart in a chart repository.
   103  
   104  When you use a chart reference with a repo prefix ('stable/mariadb'), Helm will look in the local
   105  configuration for a chart repository named 'stable', and will then look for a
   106  chart in that repository whose name is 'mariadb'. It will install the latest
   107  version of that chart unless you also supply a version number with the
   108  '--version' flag.
   109  
   110  To see the list of chart repositories, use 'helm repo list'. To search for
   111  charts in a repository, use 'helm search'.
   112  `
   113  
   114  type installCmd struct {
   115  	name           string
   116  	namespace      string
   117  	valueFiles     valueFiles
   118  	chartPath      string
   119  	dryRun         bool
   120  	disableHooks   bool
   121  	disableCRDHook bool
   122  	replace        bool
   123  	verify         bool
   124  	keyring        string
   125  	out            io.Writer
   126  	client         helm.Interface
   127  	values         []string
   128  	stringValues   []string
   129  	fileValues     []string
   130  	nameTemplate   string
   131  	version        string
   132  	timeout        int64
   133  	wait           bool
   134  	atomic         bool
   135  	repoURL        string
   136  	username       string
   137  	password       string
   138  	devel          bool
   139  	depUp          bool
   140  	subNotes       bool
   141  	description    string
   142  
   143  	certFile string
   144  	keyFile  string
   145  	caFile   string
   146  	output   string
   147  }
   148  
   149  type valueFiles []string
   150  
   151  func (v *valueFiles) String() string {
   152  	return fmt.Sprint(*v)
   153  }
   154  
   155  func (v *valueFiles) Type() string {
   156  	return "valueFiles"
   157  }
   158  
   159  func (v *valueFiles) Set(value string) error {
   160  	for _, filePath := range strings.Split(value, ",") {
   161  		*v = append(*v, filePath)
   162  	}
   163  	return nil
   164  }
   165  
   166  func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
   167  	inst := &installCmd{
   168  		out:    out,
   169  		client: c,
   170  	}
   171  
   172  	cmd := &cobra.Command{
   173  		Use:   "install [CHART]",
   174  		Short: "Install a chart archive",
   175  		Long:  installDesc,
   176  		// 在install命令行时,Helm 还执行了一个操作,使用kubectl port-forward临时在本地宿主机打通一个与Tiller Pod沟通的通道
   177  		PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
   178  		RunE: func(cmd *cobra.Command, args []string) error {
   179  			// 检查传递参数的有效性,以及是否漏传参数。
   180  			if err := checkArgsLength(len(args), "chart name"); err != nil {
   181  				return err
   182  			}
   183  
   184  			debug("Original chart version: %q", inst.version)
   185  			if inst.version == "" && inst.devel {
   186  				debug("setting version to >0.0.0-0")
   187  				inst.version = ">0.0.0-0"
   188  			}
   189  
   190  			// 寻找Chart位置,如果是本地目录,则返回并寻找完全路径;如果URL,则下载到指定路径后返回该路径名称。
   191  			cp, err := locateChartPath(inst.repoURL, inst.username, inst.password, args[0], inst.version, inst.verify, inst.keyring,
   192  				inst.certFile, inst.keyFile, inst.caFile)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			inst.chartPath = cp
   197  			// 初始化Helm Client,用来与 Tiller 通信。
   198  			inst.client = ensureHelmClient(inst.client)
   199  			inst.wait = inst.wait || inst.atomic
   200  			// 真正的业务逻辑,分别检查Chart依赖等信息,然后给Tiller发送解压后的模板信息。
   201  			return inst.run()
   202  		},
   203  	}
   204  
   205  	f := cmd.Flags()
   206  	settings.AddFlagsTLS(f)
   207  	f.VarP(&inst.valueFiles, "values", "f", "Specify values in a YAML file or a URL(can specify multiple)")
   208  	f.StringVarP(&inst.name, "name", "n", "", "The release name. If unspecified, it will autogenerate one for you")
   209  	f.StringVar(&inst.namespace, "namespace", "", "Namespace to install the release into. Defaults to the current kube config namespace.")
   210  	f.BoolVar(&inst.dryRun, "dry-run", false, "Simulate an install")
   211  	f.BoolVar(&inst.disableHooks, "no-hooks", false, "Prevent hooks from running during install")
   212  	f.BoolVar(&inst.disableCRDHook, "no-crd-hook", false, "Prevent CRD hooks from running, but run other hooks")
   213  	f.BoolVar(&inst.replace, "replace", false, "Re-use the given name, even if that name is already used. This is unsafe in production")
   214  	f.StringArrayVar(&inst.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   215  	f.StringArrayVar(&inst.stringValues, "set-string", []string{}, "Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
   216  	f.StringArrayVar(&inst.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)")
   217  	f.StringVar(&inst.nameTemplate, "name-template", "", "Specify template used to name the release")
   218  	f.BoolVar(&inst.verify, "verify", false, "Verify the package before installing it")
   219  	f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "Location of public keys used for verification")
   220  	f.StringVar(&inst.version, "version", "", "Specify the exact chart version to install. If this is not specified, the latest version is installed")
   221  	f.Int64Var(&inst.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
   222  	f.BoolVar(&inst.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")
   223  	f.BoolVar(&inst.atomic, "atomic", false, "If set, installation process purges chart on fail, also sets --wait flag")
   224  	f.StringVar(&inst.repoURL, "repo", "", "Chart repository url where to locate the requested chart")
   225  	f.StringVar(&inst.username, "username", "", "Chart repository username where to locate the requested chart")
   226  	f.StringVar(&inst.password, "password", "", "Chart repository password where to locate the requested chart")
   227  	f.StringVar(&inst.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
   228  	f.StringVar(&inst.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
   229  	f.StringVar(&inst.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
   230  	f.BoolVar(&inst.devel, "devel", false, "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
   231  	f.BoolVar(&inst.depUp, "dep-up", false, "Run helm dependency update before installing the chart")
   232  	f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent")
   233  	f.StringVar(&inst.description, "description", "", "Specify a description for the release")
   234  	bindOutputFlag(cmd, &inst.output)
   235  
   236  	// set defaults from environment
   237  	settings.InitTLS(f)
   238  
   239  	return cmd
   240  }
   241  
   242  func (i *installCmd) run() error {
   243  	debug("CHART PATH: %s\n", i.chartPath)
   244  
   245  	// 查看是否填写了命名容间名称,如果没有填写则默认是default命名空间。
   246  	if i.namespace == "" {
   247  		i.namespace = defaultNamespace()
   248  	}
   249  
   250  	// 使用vals函数合并命令行"helm install -f myvalues.yam1"覆盖的参数,这里使用的"-f override.yaml"这种命令行传参方式就是通过vals函数实现的。
   251  	// 此函数将valueFiles,values等信息读取出来后,和已经加裁到内存的valuesMap做一个对比,用外部传入的参数覆盖当前内存中的参数
   252  	// 这样在继续进行后面的动作时,都是使用的外部命令行的最新参数列表。
   253  	rawVals, err := vals(i.valueFiles, i.values, i.stringValues, i.fileValues, i.certFile, i.keyFile, i.caFile)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	// If template is specified, try to run the template.
   259  	// 如果在执行install命令时指定了template,这里就会根据template的名称使用go template模板库进行读取
   260  	// 同时也会自动渲染该模板,最终返回一个被渲染过的template对象。
   261  	if i.nameTemplate != "" {
   262  		i.name, err = generateName(i.nameTemplate)
   263  		if err != nil {
   264  			return err
   265  		}
   266  		// Print the final name so the user knows what the final name of the release is.
   267  		fmt.Printf("FINAL NAME: %s\n", i.name)
   268  	}
   269  
   270  	// 这检查指定的名称是否符合DNS命名规范,这个规范适用于Kubernetes各个资源的命名,算是Kubernetes各个资源部署的统一标准。
   271  	if msgs := validation.IsDNS1123Subdomain(i.name); i.name != "" && len(msgs) > 0 {
   272  		return fmt.Errorf("release name %s is invalid: %s", i.name, strings.Join(msgs, ";"))
   273  	}
   274  
   275  	// Check chart requirements to make sure all dependencies are present in /charts
   276  	// 根据前面返回的Chart本地存储路径加载对应的Chart文件。这里的Chart文件一般都是一个文件夹,里面含有values.yaml、Chart等各种文件和文件夹
   277  	// 该函数读取这些文件的内容后,将其初始化为一个对应的Chart,这样既能校验Chart内容的正确性也方便后面继续调用。
   278  	// 如果设置了.helmignore 文件,那么这个函数也会略过这些文件,不会将其序列化到Chart对象中。
   279  	chartRequested, err := chartutil.Load(i.chartPath)
   280  	if err != nil {
   281  		return prettyError(err)
   282  	}
   283  
   284  	if chartRequested.Metadata.Deprecated {
   285  		fmt.Fprintln(os.Stderr, "WARNING: This chart is deprecated")
   286  	}
   287  
   288  	// 检查是否有requirements.yaml 文件,并且将声明的文件内容使用上面介绍的Chart重新下载和读取渲染函数来重新运行一遍。
   289  	// 全部下载完毕后,还会再使用Load函数加载内容加载。到此,内存中的Chart结构体已经包含所有需要的文本信息。
   290  	if req, err := chartutil.LoadRequirements(chartRequested); err == nil {
   291  		// If checkDependencies returns an error, we have unfulfilled dependencies.
   292  		// As of Helm 2.4.0, this is treated as a stopping condition:
   293  		// https://github.com/kubernetes/helm/issues/2209
   294  		if err := renderutil.CheckDependencies(chartRequested, req); err != nil {
   295  			if i.depUp {
   296  				man := &downloader.Manager{
   297  					Out:        i.out,
   298  					ChartPath:  i.chartPath,
   299  					HelmHome:   settings.Home,
   300  					Keyring:    defaultKeyring(),
   301  					SkipUpdate: false,
   302  					Getters:    getter.All(settings),
   303  				}
   304  				if err := man.Update(); err != nil {
   305  					return prettyError(err)
   306  				}
   307  
   308  				// Update all dependencies which are present in /charts.
   309  				chartRequested, err = chartutil.Load(i.chartPath)
   310  				if err != nil {
   311  					return prettyError(err)
   312  				}
   313  			} else {
   314  				return prettyError(err)
   315  			}
   316  
   317  		}
   318  	} else if err != chartutil.ErrRequirementsNotFound {
   319  		return fmt.Errorf("cannot load requirements: %v", err)
   320  	}
   321  
   322  	res, err := i.client.InstallReleaseFromChart(
   323  		chartRequested,
   324  		i.namespace,
   325  		helm.ValueOverrides(rawVals),
   326  		helm.ReleaseName(i.name),
   327  		helm.InstallDryRun(i.dryRun),
   328  		helm.InstallReuseName(i.replace),
   329  		helm.InstallDisableHooks(i.disableHooks),
   330  		helm.InstallDisableCRDHook(i.disableCRDHook),
   331  		helm.InstallSubNotes(i.subNotes),
   332  		helm.InstallTimeout(i.timeout),
   333  		helm.InstallWait(i.wait),
   334  		helm.InstallDescription(i.description))
   335  	if err != nil {
   336  		if i.atomic {
   337  			fmt.Fprintf(os.Stdout, "INSTALL FAILED\nPURGING CHART\nError: %v\n", prettyError(err))
   338  			deleteSideEffects := &deleteCmd{
   339  				name:         i.name,
   340  				disableHooks: i.disableHooks,
   341  				purge:        true,
   342  				timeout:      i.timeout,
   343  				description:  "",
   344  				dryRun:       i.dryRun,
   345  				out:          i.out,
   346  				client:       i.client,
   347  			}
   348  			if err := deleteSideEffects.run(); err != nil {
   349  				return err
   350  			}
   351  			fmt.Fprintf(os.Stdout, "Successfully purged a chart!\n")
   352  		}
   353  		return prettyError(err)
   354  	}
   355  
   356  	// 首先获取Release信息,在上步发送install请求后,Tiller的response信息内就已经含有了这些信息。
   357  	rel := res.GetRelease()
   358  	if rel == nil {
   359  		return nil
   360  	}
   361  
   362  	if outputFormat(i.output) == outputTable {
   363  		i.printRelease(rel)
   364  	}
   365  
   366  	// If this is a dry run, we can't display status.
   367  	if i.dryRun {
   368  		// This is special casing to avoid breaking backward compatibility:
   369  		if res.Release.Info.Description != "Dry run complete" {
   370  			fmt.Fprintf(os.Stdout, "WARNING: %s\n", res.Release.Info.Description)
   371  		}
   372  		return nil
   373  	}
   374  
   375  	// Print the status like status command does
   376  	// 和install一样, 也是向Tiller发送请求,这个API的URL /hapi.services.tiller.ReleaseService/GetReleaseStatus
   377  	status, err := i.client.ReleaseStatus(rel.Name)
   378  	if err != nil {
   379  		return prettyError(err)
   380  	}
   381  
   382  	return write(i.out, &statusWriter{status}, outputFormat(i.output))
   383  }
   384  
   385  // Merges source and destination map, preferring values from the source map
   386  func mergeValues(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
   387  	for k, v := range src {
   388  		// If the key doesn't exist already, then just set the key to that value
   389  		if _, exists := dest[k]; !exists {
   390  			dest[k] = v
   391  			continue
   392  		}
   393  		nextMap, ok := v.(map[string]interface{})
   394  		// If it isn't another map, overwrite the value
   395  		if !ok {
   396  			dest[k] = v
   397  			continue
   398  		}
   399  		// Edge case: If the key exists in the destination, but isn't a map
   400  		destMap, isMap := dest[k].(map[string]interface{})
   401  		// If the source map has a map for this key, prefer it
   402  		if !isMap {
   403  			dest[k] = v
   404  			continue
   405  		}
   406  		// If we got to this point, it is a map in both, so merge them
   407  		dest[k] = mergeValues(destMap, nextMap)
   408  	}
   409  	return dest
   410  }
   411  
   412  // vals merges values from files specified via -f/--values and
   413  // directly via --set or --set-string or --set-file, marshaling them to YAML
   414  func vals(valueFiles valueFiles, values []string, stringValues []string, fileValues []string, CertFile, KeyFile, CAFile string) ([]byte, error) {
   415  	base := map[string]interface{}{}
   416  
   417  	// User specified a values files via -f/--values
   418  	for _, filePath := range valueFiles {
   419  		currentMap := map[string]interface{}{}
   420  
   421  		var bytes []byte
   422  		var err error
   423  		if strings.TrimSpace(filePath) == "-" {
   424  			bytes, err = ioutil.ReadAll(os.Stdin)
   425  		} else {
   426  			bytes, err = readFile(filePath, CertFile, KeyFile, CAFile)
   427  		}
   428  
   429  		if err != nil {
   430  			return []byte{}, err
   431  		}
   432  
   433  		if err := yaml.Unmarshal(bytes, &currentMap); err != nil {
   434  			return []byte{}, fmt.Errorf("failed to parse %s: %s", filePath, err)
   435  		}
   436  		// Merge with the previous map
   437  		base = mergeValues(base, currentMap)
   438  	}
   439  
   440  	// User specified a value via --set
   441  	for _, value := range values {
   442  		if err := strvals.ParseInto(value, base); err != nil {
   443  			return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
   444  		}
   445  	}
   446  
   447  	// User specified a value via --set-string
   448  	for _, value := range stringValues {
   449  		if err := strvals.ParseIntoString(value, base); err != nil {
   450  			return []byte{}, fmt.Errorf("failed parsing --set-string data: %s", err)
   451  		}
   452  	}
   453  
   454  	// User specified a value via --set-file
   455  	for _, value := range fileValues {
   456  		reader := func(rs []rune) (interface{}, error) {
   457  			bytes, err := readFile(string(rs), CertFile, KeyFile, CAFile)
   458  			return string(bytes), err
   459  		}
   460  		if err := strvals.ParseIntoFile(value, base, reader); err != nil {
   461  			return []byte{}, fmt.Errorf("failed parsing --set-file data: %s", err)
   462  		}
   463  	}
   464  
   465  	return yaml.Marshal(base)
   466  }
   467  
   468  // printRelease prints info about a release if the Debug is true.
   469  func (i *installCmd) printRelease(rel *release.Release) {
   470  	if rel == nil {
   471  		return
   472  	}
   473  	// TODO: Switch to text/template like everything else.
   474  	fmt.Fprintf(i.out, "NAME:   %s\n", rel.Name)
   475  	if settings.Debug {
   476  		printRelease(i.out, rel)
   477  	}
   478  }
   479  
   480  // locateChartPath looks for a chart directory in known places, and returns either the full path or an error.
   481  //
   482  // This does not ensure that the chart is well-formed; only that the requested filename exists.
   483  //
   484  // Order of resolution:
   485  // - current working directory
   486  // - if path is absolute or begins with '.', error out here
   487  // - chart repos in $HELM_HOME
   488  // - URL
   489  //
   490  // If 'verify' is true, this will attempt to also verify the chart.
   491  func locateChartPath(repoURL, username, password, name, version string, verify bool, keyring,
   492  	certFile, keyFile, caFile string) (string, error) {
   493  	// 第一优先级是当前目录,对传入的目录去掉左右空行后,通过库函数判断当前文件夹是否存在,如果存在,则返回当前文件夹的全局绝对路径
   494  	name = strings.TrimSpace(name)
   495  	version = strings.TrimSpace(version)
   496  	if fi, err := os.Stat(name); err == nil {
   497  		abs, err := filepath.Abs(name)
   498  		if err != nil {
   499  			return abs, err
   500  		}
   501  		if verify {
   502  			if fi.IsDir() {
   503  				return "", errors.New("cannot verify a directory")
   504  			}
   505  			if _, err := downloader.VerifyChart(abs, keyring); err != nil {
   506  				return "", err
   507  			}
   508  		}
   509  		return abs, nil
   510  	}
   511  	// 如果传入的是绝对路径或者是以 . 开头的路径,则直接返回失败
   512  	if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
   513  		return name, fmt.Errorf("path %q not found", name)
   514  	}
   515  
   516  	// 如果前面两种都不是,则从 HELM_HOME 中寻找对应的文件,并返回对应的绝对路径
   517  	crepo := filepath.Join(settings.Home.Repository(), name)
   518  	if _, err := os.Stat(crepo); err == nil {
   519  		return filepath.Abs(crepo)
   520  	}
   521  
   522  	// 除了如上3种情况外,下面提供一个URL,需要从URL下载
   523  	dl := downloader.ChartDownloader{
   524  		HelmHome: settings.Home,
   525  		Out:      os.Stdout,
   526  		Keyring:  keyring,
   527  		Getters:  getter.All(settings),
   528  		Username: username,
   529  		Password: password,
   530  	}
   531  	if verify {
   532  		dl.Verify = downloader.VerifyAlways
   533  	}
   534  	// 如果设置了Chart repo,就会从对应的chart repo处查找Chart、
   535  	if repoURL != "" {
   536  		chartURL, err := repo.FindChartInAuthRepoURL(repoURL, username, password, name, version,
   537  			certFile, keyFile, caFile, getter.All(settings))
   538  		if err != nil {
   539  			return "", err
   540  		}
   541  		name = chartURL
   542  	}
   543  
   544  	// 如果默认设置的下载路径没有对应的文件夹,那么先创建对应的文件夹。
   545  	if _, err := os.Stat(settings.Home.Archive()); os.IsNotExist(err) {
   546  		os.MkdirAll(settings.Home.Archive(), 0744)
   547  	}
   548  
   549  	// 将前面拼接好的带有URL路径的文件,或者用户直接提供的全路径http文件下载到对应的文件夹中,最后返回这个下载到本地路径的文件。
   550  	filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive())
   551  	if err == nil {
   552  		lname, err := filepath.Abs(filename)
   553  		if err != nil {
   554  			return filename, err
   555  		}
   556  		debug("Fetched %s to %s\n", name, filename)
   557  		return lname, nil
   558  	} else if settings.Debug {
   559  		return filename, err
   560  	}
   561  
   562  	return filename, fmt.Errorf("failed to download %q (hint: running `helm repo update` may help)", name)
   563  }
   564  
   565  func generateName(nameTemplate string) (string, error) {
   566  	t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate)
   567  	if err != nil {
   568  		return "", err
   569  	}
   570  	var b bytes.Buffer
   571  	err = t.Execute(&b, nil)
   572  	if err != nil {
   573  		return "", err
   574  	}
   575  	return b.String(), nil
   576  }
   577  
   578  func defaultNamespace() string {
   579  	if ns, _, err := kube.GetConfig(settings.KubeContext, settings.KubeConfig).Namespace(); err == nil {
   580  		return ns
   581  	}
   582  	return "default"
   583  }
   584  
   585  //readFile load a file from the local directory or a remote file with a url.
   586  func readFile(filePath, CertFile, KeyFile, CAFile string) ([]byte, error) {
   587  	u, _ := url.Parse(filePath)
   588  	p := getter.All(settings)
   589  
   590  	// FIXME: maybe someone handle other protocols like ftp.
   591  	getterConstructor, err := p.ByScheme(u.Scheme)
   592  
   593  	if err != nil {
   594  		return ioutil.ReadFile(filePath)
   595  	}
   596  
   597  	getter, err := getterConstructor(filePath, CertFile, KeyFile, CAFile)
   598  	if err != nil {
   599  		return []byte{}, err
   600  	}
   601  	data, err := getter.Get(filePath)
   602  	return data.Bytes(), err
   603  }