github.com/splunk/dan1-qbec@v0.7.3/internal/vm/helm.go (about)

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