github.com/argoproj/argo-cd@v1.8.7/util/helm/cmd.go (about)

     1  package helm
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path"
     9  	"path/filepath"
    10  	"regexp"
    11  
    12  	executil "github.com/argoproj/argo-cd/util/exec"
    13  	"github.com/argoproj/argo-cd/util/io"
    14  )
    15  
    16  // A thin wrapper around the "helm" command, adding logging and error translation.
    17  type Cmd struct {
    18  	HelmVer
    19  	helmHome  string
    20  	WorkDir   string
    21  	IsLocal   bool
    22  	IsHelmOci bool
    23  }
    24  
    25  func NewCmd(workDir string, version string) (*Cmd, error) {
    26  	if version != "" {
    27  		switch version {
    28  		case "v2":
    29  			return NewCmdWithVersion(workDir, HelmV2, false)
    30  		case "v3":
    31  			return NewCmdWithVersion(workDir, HelmV3, false)
    32  		}
    33  	}
    34  	helmVersion, err := getHelmVersion(workDir)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	return NewCmdWithVersion(workDir, *helmVersion, false)
    40  }
    41  
    42  func NewCmdWithVersion(workDir string, version HelmVer, isHelmOci bool) (*Cmd, error) {
    43  	tmpDir, err := ioutil.TempDir("", "helm")
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	return &Cmd{WorkDir: workDir, helmHome: tmpDir, HelmVer: version, IsHelmOci: isHelmOci}, err
    48  }
    49  
    50  var redactor = func(text string) string {
    51  	return regexp.MustCompile("(--username|--password) [^ ]*").ReplaceAllString(text, "$1 ******")
    52  }
    53  
    54  func (c Cmd) run(args ...string) (string, error) {
    55  	cmd := exec.Command(c.binaryName, args...)
    56  	cmd.Dir = c.WorkDir
    57  	cmd.Env = os.Environ()
    58  	if !c.IsLocal {
    59  		cmd.Env = append(cmd.Env,
    60  			fmt.Sprintf("XDG_CACHE_HOME=%s/cache", c.helmHome),
    61  			fmt.Sprintf("XDG_CONFIG_HOME=%s/config", c.helmHome),
    62  			fmt.Sprintf("XDG_DATA_HOME=%s/data", c.helmHome),
    63  			fmt.Sprintf("HELM_HOME=%s", c.helmHome))
    64  	}
    65  
    66  	if c.IsHelmOci {
    67  		cmd.Env = append(cmd.Env, "HELM_EXPERIMENTAL_OCI=1")
    68  	}
    69  	return executil.RunWithRedactor(cmd, redactor)
    70  }
    71  
    72  func (c *Cmd) Init() (string, error) {
    73  	if c.initSupported {
    74  		return c.run("init", "--client-only", "--skip-refresh")
    75  	}
    76  	return "", nil
    77  }
    78  
    79  func (c *Cmd) Login(repo string, creds Creds) (string, error) {
    80  	args := []string{"registry", "login"}
    81  	args = append(args, repo)
    82  
    83  	if creds.Username != "" {
    84  		args = append(args, "--username", creds.Username)
    85  	}
    86  
    87  	if creds.Password != "" {
    88  		args = append(args, "--password", creds.Password)
    89  	}
    90  
    91  	if creds.CAPath != "" {
    92  		args = append(args, "--ca-file", creds.CAPath)
    93  	}
    94  	if len(creds.CertData) > 0 {
    95  		filePath, closer, err := writeToTmp(creds.CertData)
    96  		if err != nil {
    97  			return "", err
    98  		}
    99  		defer io.Close(closer)
   100  		args = append(args, "--cert-file", filePath)
   101  	}
   102  	if len(creds.KeyData) > 0 {
   103  		filePath, closer, err := writeToTmp(creds.KeyData)
   104  		if err != nil {
   105  			return "", err
   106  		}
   107  		defer io.Close(closer)
   108  		args = append(args, "--key-file", filePath)
   109  	}
   110  
   111  	if creds.InsecureSkipVerify {
   112  		args = append(args, "--insecure")
   113  	}
   114  	return c.run(args...)
   115  }
   116  
   117  func (c *Cmd) Logout(repo string, creds Creds) (string, error) {
   118  	args := []string{"registry", "logout"}
   119  	args = append(args, repo)
   120  
   121  	if creds.CAPath != "" {
   122  		args = append(args, "--ca-file", creds.CAPath)
   123  	}
   124  	if len(creds.CertData) > 0 {
   125  		filePath, closer, err := writeToTmp(creds.CertData)
   126  		if err != nil {
   127  			return "", err
   128  		}
   129  		defer io.Close(closer)
   130  		args = append(args, "--cert-file", filePath)
   131  	}
   132  	if len(creds.KeyData) > 0 {
   133  		filePath, closer, err := writeToTmp(creds.KeyData)
   134  		if err != nil {
   135  			return "", err
   136  		}
   137  		defer io.Close(closer)
   138  		args = append(args, "--key-file", filePath)
   139  	}
   140  
   141  	return c.run(args...)
   142  }
   143  
   144  func (c *Cmd) RepoAdd(name string, url string, opts Creds) (string, error) {
   145  	tmp, err := ioutil.TempDir("", "helm")
   146  	if err != nil {
   147  		return "", err
   148  	}
   149  	defer func() { _ = os.RemoveAll(tmp) }()
   150  
   151  	args := []string{"repo", "add"}
   152  
   153  	if opts.Username != "" {
   154  		args = append(args, "--username", opts.Username)
   155  	}
   156  
   157  	if opts.Password != "" {
   158  		args = append(args, "--password", opts.Password)
   159  	}
   160  
   161  	if opts.CAPath != "" {
   162  		args = append(args, "--ca-file", opts.CAPath)
   163  	}
   164  
   165  	if opts.InsecureSkipVerify && c.insecureSkipVerifySupported {
   166  		args = append(args, "--insecure-skip-tls-verify")
   167  	}
   168  
   169  	if len(opts.CertData) > 0 {
   170  		certFile, err := ioutil.TempFile("", "helm")
   171  		if err != nil {
   172  			return "", err
   173  		}
   174  		_, err = certFile.Write(opts.CertData)
   175  		if err != nil {
   176  			return "", err
   177  		}
   178  		args = append(args, "--cert-file", certFile.Name())
   179  	}
   180  
   181  	if len(opts.KeyData) > 0 {
   182  		keyFile, err := ioutil.TempFile("", "helm")
   183  		if err != nil {
   184  			return "", err
   185  		}
   186  		_, err = keyFile.Write(opts.KeyData)
   187  		if err != nil {
   188  			return "", err
   189  		}
   190  		args = append(args, "--key-file", keyFile.Name())
   191  	}
   192  
   193  	args = append(args, name, url)
   194  
   195  	return c.run(args...)
   196  }
   197  
   198  func writeToTmp(data []byte) (string, io.Closer, error) {
   199  	file, err := ioutil.TempFile("", "")
   200  	if err != nil {
   201  		return "", nil, err
   202  	}
   203  	err = ioutil.WriteFile(file.Name(), data, 0644)
   204  	if err != nil {
   205  		_ = os.RemoveAll(file.Name())
   206  		return "", nil, err
   207  	}
   208  	return file.Name(), io.NewCloser(func() error {
   209  		return os.RemoveAll(file.Name())
   210  	}), nil
   211  }
   212  
   213  func (c *Cmd) Fetch(repo, chartName, version, destination string, creds Creds) (string, error) {
   214  	args := []string{c.pullCommand, "--destination", destination}
   215  	if version != "" {
   216  		args = append(args, "--version", version)
   217  	}
   218  	if creds.Username != "" {
   219  		args = append(args, "--username", creds.Username)
   220  	}
   221  	if creds.Password != "" {
   222  		args = append(args, "--password", creds.Password)
   223  	}
   224  
   225  	args = append(args, "--repo", repo, chartName)
   226  
   227  	if creds.CAPath != "" {
   228  		args = append(args, "--ca-file", creds.CAPath)
   229  	}
   230  	if len(creds.CertData) > 0 {
   231  		filePath, closer, err := writeToTmp(creds.CertData)
   232  		if err != nil {
   233  			return "", err
   234  		}
   235  		defer io.Close(closer)
   236  		args = append(args, "--cert-file", filePath)
   237  	}
   238  	if len(creds.KeyData) > 0 {
   239  		filePath, closer, err := writeToTmp(creds.KeyData)
   240  		if err != nil {
   241  			return "", err
   242  		}
   243  		defer io.Close(closer)
   244  		args = append(args, "--key-file", filePath)
   245  	}
   246  
   247  	return c.run(args...)
   248  }
   249  
   250  func (c *Cmd) ChartPull(repo string, chart string, version string) (string, error) {
   251  	return c.run("chart", "pull", fmt.Sprintf("%s/%s:%s", repo, chart, version))
   252  }
   253  
   254  func (c *Cmd) ChartExport(repo string, chartName string, version string, destination string) (string, error) {
   255  	args := []string{"chart", "export"}
   256  	chartURL := fmt.Sprintf(repo + "/" + chartName + ":" + version)
   257  	args = append(args, chartURL, "--destination", destination)
   258  
   259  	return c.run(args...)
   260  }
   261  
   262  func (c *Cmd) dependencyBuild() (string, error) {
   263  	return c.run("dependency", "build")
   264  }
   265  
   266  func (c *Cmd) inspectValues(values string) (string, error) {
   267  	return c.run(c.showCommand, "values", values)
   268  }
   269  
   270  type TemplateOpts struct {
   271  	Name        string
   272  	Namespace   string
   273  	KubeVersion string
   274  	APIVersions []string
   275  	Set         map[string]string
   276  	SetString   map[string]string
   277  	SetFile     map[string]string
   278  	Values      []string
   279  }
   280  
   281  var (
   282  	re = regexp.MustCompile(`([^\\]),`)
   283  )
   284  
   285  func cleanSetParameters(val string) string {
   286  	return re.ReplaceAllString(val, `$1\,`)
   287  }
   288  
   289  func (c *Cmd) template(chartPath string, opts *TemplateOpts) (string, error) {
   290  	if c.HelmVer.getPostTemplateCallback != nil {
   291  		if callback, err := c.HelmVer.getPostTemplateCallback(filepath.Clean(path.Join(c.WorkDir, chartPath))); err == nil {
   292  			defer callback()
   293  		} else {
   294  			return "", err
   295  		}
   296  	}
   297  
   298  	args := []string{"template", chartPath, c.templateNameArg, opts.Name}
   299  
   300  	if opts.Namespace != "" {
   301  		args = append(args, "--namespace", opts.Namespace)
   302  	}
   303  	if opts.KubeVersion != "" && c.kubeVersionSupported {
   304  		args = append(args, "--kube-version", opts.KubeVersion)
   305  	}
   306  	for key, val := range opts.Set {
   307  		args = append(args, "--set", key+"="+cleanSetParameters(val))
   308  	}
   309  	for key, val := range opts.SetString {
   310  		args = append(args, "--set-string", key+"="+cleanSetParameters(val))
   311  	}
   312  	for key, val := range opts.SetFile {
   313  		args = append(args, "--set-file", key+"="+cleanSetParameters(val))
   314  	}
   315  	for _, val := range opts.Values {
   316  		args = append(args, "--values", val)
   317  	}
   318  	for _, v := range opts.APIVersions {
   319  		args = append(args, "--api-versions", v)
   320  	}
   321  	if c.HelmVer.additionalTemplateArgs != nil {
   322  		args = append(args, c.HelmVer.additionalTemplateArgs...)
   323  	}
   324  
   325  	return c.run(args...)
   326  }
   327  
   328  func (c *Cmd) Close() {
   329  	_ = os.RemoveAll(c.helmHome)
   330  }