github.com/argoproj/argo-cd@v1.8.7/util/helm/helm.go (about) 1 package helm 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/url" 7 "os/exec" 8 "path" 9 "strings" 10 11 "github.com/ghodss/yaml" 12 13 "github.com/argoproj/argo-cd/util/config" 14 executil "github.com/argoproj/argo-cd/util/exec" 15 ) 16 17 type HelmRepository struct { 18 Creds 19 Name string 20 Repo string 21 } 22 23 // Helm provides wrapper functionality around the `helm` command. 24 type Helm interface { 25 // Template returns a list of unstructured objects from a `helm template` command 26 Template(opts *TemplateOpts) (string, error) 27 // GetParameters returns a list of chart parameters taking into account values in provided YAML files. 28 GetParameters(valuesFiles []string) (map[string]string, error) 29 // DependencyBuild runs `helm dependency build` to download a chart's dependencies 30 DependencyBuild() error 31 // Init runs `helm init --client-only` 32 Init() error 33 // Dispose deletes temp resources 34 Dispose() 35 } 36 37 // NewHelmApp create a new wrapper to run commands on the `helm` command-line tool. 38 func NewHelmApp(workDir string, repos []HelmRepository, isLocal bool, version string) (Helm, error) { 39 cmd, err := NewCmd(workDir, version) 40 if err != nil { 41 return nil, err 42 } 43 cmd.IsLocal = isLocal 44 45 return &helm{repos: repos, cmd: *cmd}, nil 46 } 47 48 type helm struct { 49 cmd Cmd 50 repos []HelmRepository 51 } 52 53 // IsMissingDependencyErr tests if the error is related to a missing chart dependency 54 func IsMissingDependencyErr(err error) bool { 55 return strings.Contains(err.Error(), "found in requirements.yaml, but missing in charts") || 56 strings.Contains(err.Error(), "found in Chart.yaml, but missing in charts/ directory") 57 } 58 59 func (h *helm) Template(templateOpts *TemplateOpts) (string, error) { 60 out, err := h.cmd.template(".", templateOpts) 61 if err != nil { 62 return "", err 63 } 64 return out, nil 65 } 66 67 func (h *helm) DependencyBuild() error { 68 for _, repo := range h.repos { 69 _, err := h.cmd.RepoAdd(repo.Name, repo.Repo, repo.Creds) 70 71 if err != nil { 72 return err 73 } 74 } 75 h.repos = nil 76 _, err := h.cmd.dependencyBuild() 77 return err 78 } 79 80 func (h *helm) Init() error { 81 _, err := h.cmd.Init() 82 return err 83 } 84 85 func (h *helm) Dispose() { 86 h.cmd.Close() 87 } 88 89 func Version(shortForm bool) (string, error) { 90 executable := "helm" 91 cmdArgs := []string{"version", "--client"} 92 if shortForm { 93 cmdArgs = append(cmdArgs, "--short") 94 } 95 cmd := exec.Command(executable, cmdArgs...) 96 // example version output: 97 // long: "version.BuildInfo{Version:\"v3.3.1\", GitCommit:\"249e5215cde0c3fa72e27eb7a30e8d55c9696144\", GitTreeState:\"clean\", GoVersion:\"go1.14.7\"}" 98 // short: "v3.3.1+g249e521" 99 version, err := executil.RunWithRedactor(cmd, redactor) 100 if err != nil { 101 return "", fmt.Errorf("could not get helm version: %s", err) 102 } 103 return strings.TrimSpace(version), nil 104 } 105 106 func (h *helm) GetParameters(valuesFiles []string) (map[string]string, error) { 107 out, err := h.cmd.inspectValues(".") 108 if err != nil { 109 return nil, err 110 } 111 values := []string{out} 112 for _, file := range valuesFiles { 113 var fileValues []byte 114 parsedURL, err := url.ParseRequestURI(file) 115 if err == nil && (parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { 116 fileValues, err = config.ReadRemoteFile(file) 117 } else { 118 fileValues, err = ioutil.ReadFile(path.Join(h.cmd.WorkDir, file)) 119 } 120 if err != nil { 121 return nil, fmt.Errorf("failed to read value file %s: %s", file, err) 122 } 123 values = append(values, string(fileValues)) 124 } 125 126 output := map[string]string{} 127 for _, file := range values { 128 values := map[string]interface{}{} 129 if err = yaml.Unmarshal([]byte(file), &values); err != nil { 130 return nil, fmt.Errorf("failed to parse values: %s", err) 131 } 132 flatVals(values, output) 133 } 134 135 return output, nil 136 } 137 138 func flatVals(input interface{}, output map[string]string, prefixes ...string) { 139 switch i := input.(type) { 140 case map[string]interface{}: 141 for k, v := range i { 142 flatVals(v, output, append(prefixes, k)...) 143 } 144 case []interface{}: 145 for j, v := range i { 146 flatVals(v, output, append(prefixes[0:len(prefixes)-1], fmt.Sprintf("%s[%v]", prefixes[len(prefixes)-1], j))...) 147 } 148 default: 149 output[strings.Join(prefixes, ".")] = fmt.Sprintf("%v", i) 150 } 151 }