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 }