Skip to content
Snippets Groups Projects
cmd.go 3.42 KiB
Newer Older
Florent Aide's avatar
Florent Aide committed
package runner

import (
steeve.chailloux's avatar
steeve.chailloux committed
	"os"
	"path/filepath"
steeve.chailloux's avatar
steeve.chailloux committed

Florent Aide's avatar
Florent Aide committed
	"github.com/go-cmd/cmd"
steeve.chailloux's avatar
steeve.chailloux committed
	"github.com/rs/zerolog"
Florent Aide's avatar
Florent Aide committed
)

func RunCMD(name string, args ...string) (err error, stdout, stderr []string) {
steeve.chailloux's avatar
steeve.chailloux committed
	// helm template -f base.yaml -f base.values.yaml -f ns.yaml -f ns.values.yaml
	// ytt -f /chart-folder -f base.yaml -f ns.yaml -v ... -v ...
Florent Aide's avatar
Florent Aide committed
	c := cmd.NewCmd(name, args...)
	statusChan := c.Start()
	status := <-statusChan
	if status.Error != nil {
		return err, status.Stdout, status.Stderr
	}
	stdout = status.Stdout
	stderr = status.Stderr
	return
}
steeve.chailloux's avatar
steeve.chailloux committed

func NewCmdConfig(logger zerolog.Logger, configDir string, namespace string) (*CmdConfig, error) {
	var cmdConfig CmdConfig
	cmdConfig.Namespace = namespace
	cmdConfig.Logger = logger

	baseCfg, err := NewConfig(configDir)
	if err != nil {
		return nil, err
	}

	nsCfgDir := filepath.Join(configDir, namespace)
	nsCfg, err := NewConfig(nsCfgDir)
	if err != nil && err != os.ErrNotExist {
		return nil, err
	}

	// TODO:
	// - merge baseCfg & nsCfg according to magic
	// - hydrate
	return &cmdConfig, nil
}

type CmdConfig struct {
	Spec      CmdSpec
	Namespace string
	Logger    zerolog.Logger
}

type CmdSpec struct {
	Variables []Variable
	Charts    CmdChart
}

type CmdChart struct {
	Helm map[string]CmdHelmChart
	Ytt  map[string]CmdYttChart
}

type CmdHelmChart struct {
	Type   string
	Name   string
	Values []string
}
type CmdYttChart struct {
	Type   string
	Name   string
	Files  []string
	Values []Value
}

// hydrate expands templated variables in our config with concrete values
func (c *CmdConfig) hydrate() error {
	if err := c.hydrateHelmCharts(); err != nil {
		return err
	}
	if err := c.hydrateYttCharts(); err != nil {
		return err
	}
	return nil
}

func (c *CmdConfig) prepareVariables(v []Variable) map[string]string {
	variables := make(map[string]string)
	for _, variable := range v {
		variables[variable.Name] = variable.Value
	}
	variables["namespace"] = c.Namespace
	return variables
}

func (c *CmdConfig) hydrateYttCharts() error {
	for entryFileName, entry := range c.Spec.Charts.Ytt {
		for valIndex, val := range entry.Values {
			valueTmpl, err := template.New("ytt entry value").Parse(val.Value)
			if err != nil {
				return fmt.Errorf("failed to parse ytt entry value as template: %q, %w", val.Value, err)
			}
			buf := new(bytes.Buffer)
			if err := valueTmpl.Execute(buf, c.prepareVariables(c.Spec.Variables)); err != nil {
				return fmt.Errorf("failed to hydrate ytt entry: %q, %w", val.Value, err)
			}
			// replace original content with hydrated version
			c.Spec.Charts.Ytt[entryFileName].Values[valIndex].Value = buf.String()
		}
	}
	return nil
}

func (c *CmdConfig) hydrateHelmCharts() error {
	for name, chart := range c.Spec.Charts.Helm {
		var newVals []string
		for _, value := range chart.Values {
			rawChartValue, err := yaml.Marshal(value)
			if err != nil {
				return fmt.Errorf("failed to get chart values as string: %w", err)
			}
			valueTmpl, err := template.New("chart").Parse(string(rawChartValue))
			if err != nil {
				return fmt.Errorf("failed to parse chart values as template: %q, %w", chart.Values, err)
			}
			buf := new(bytes.Buffer)
			if err := valueTmpl.Execute(buf, c.prepareVariables(c.Spec.Variables)); err != nil {
				return fmt.Errorf("failed to hydrate chart values entry: %q, %w", chart.Values, err)
			}
			newVals = append(newVals, buf.String())
		}
		chart.Values = newVals
		c.Spec.Charts.Helm[name] = chart
	}
	return nil
}