github.com/splunk/qbec@v0.15.2/vm/internal/natives/helm.go (about) 1 /* 2 Copyright 2021 Splunk Inc. 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 natives 18 19 import ( 20 "bytes" 21 "fmt" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "strings" 26 27 "github.com/ghodss/yaml" 28 "github.com/pkg/errors" 29 ) 30 31 // helmOptions are options that can be passed to the helm template command as well 32 // as a `thisFile` option that the caller needs to set from `std.thisFile` to make 33 // relative references to charts work correctly. 34 type helmOptions struct { 35 Execute []string `json:"execute"` // --execute option 36 KubeVersion string `json:"kubeVersion"` // --kube-version option 37 Name string `json:"name"` // --name option 38 NameTemplate string `json:"nameTemplate"` // --name-template option 39 Namespace string `json:"namespace"` // --namespace option 40 ThisFile string `json:"thisFile"` // use supplied file as current file to resolve relative refs, should be set to std.thisFile 41 Verbose bool `json:"verbose"` // print helm template command before executing it 42 //IsUpgrade bool `json:"isUpgrade"` // --is-upgrade option, defer adding this until implications are known, 43 } 44 45 // toArgs converts options to a slice of command-line args. 46 func (h helmOptions) toArgs() []string { 47 var ret []string 48 if len(h.Execute) > 0 { 49 for _, e := range h.Execute { 50 ret = append(ret, "--execute", e) 51 } 52 } 53 if h.KubeVersion != "" { 54 ret = append(ret, "--kube-version", h.KubeVersion) 55 } 56 if h.Name != "" { 57 ret = append(ret, "--name", h.Name) 58 } 59 if h.NameTemplate != "" { 60 ret = append(ret, "--name-template", h.NameTemplate) 61 } 62 if h.Namespace != "" { 63 ret = append(ret, "--namespace", h.Namespace) 64 } 65 //if h.IsUpgrade { 66 // ret = append(ret, "--is-upgrade") 67 //} 68 return ret 69 } 70 71 // expandHelmTemplate produces an array of objects parsed from the output of running `helm template` with 72 // the supplied values and helm options. 73 func expandHelmTemplate(chart string, values map[string]interface{}, options helmOptions) (out []interface{}, finalErr error) { 74 // run command from the directory containing current file or the OS temp dir if `thisFile` not specified. That is, 75 // explicitly fail to resolve relative refs unless the calling file is specified; don't let them work by happenstance. 76 workDir := os.TempDir() 77 if options.ThisFile != "" { 78 dir := filepath.Dir(options.ThisFile) 79 if !filepath.IsAbs(dir) { 80 wd, err := os.Getwd() 81 if err != nil { 82 return nil, errors.Wrap(err, "get working directory") 83 } 84 dir = filepath.Join(wd, dir) 85 } 86 workDir = dir 87 } 88 89 valueBytes, err := yaml.Marshal(values) 90 if err != nil { 91 return nil, errors.Wrap(err, "marshal values to YAML") 92 } 93 94 args := append([]string{"template", chart}, options.toArgs()...) 95 args = append(args, "--values", "-") 96 97 var stdout bytes.Buffer 98 cmd := exec.Command("helm", args...) 99 cmd.Stdin = bytes.NewBuffer(valueBytes) 100 cmd.Stdout = &stdout 101 cmd.Stderr = os.Stderr 102 cmd.Dir = workDir 103 104 if options.Verbose { 105 fmt.Fprintf(os.Stderr, "[helm template] cd %s && helm %s\n", workDir, strings.Join(args, " ")) 106 } 107 108 if err := cmd.Run(); err != nil { 109 if options.ThisFile == "" { 110 fmt.Fprintln(os.Stderr, "[WARN] helm template command failed, you may need to set the 'thisFile' option to make relative chart paths work") 111 } 112 return nil, errors.Wrap(err, "run helm template command") 113 } 114 115 return ParseYAMLDocuments(bytes.NewReader(stdout.Bytes())) 116 }