Skip to content
Snippets Groups Projects
config.go 3.46 KiB
Newer Older
Florent Aide's avatar
Florent Aide committed
	"bytes"
steeve.chailloux's avatar
steeve.chailloux committed
	"errors"
Florent Aide's avatar
Florent Aide committed
	"fmt"
steeve.chailloux's avatar
steeve.chailloux committed
	"os"
Florent Aide's avatar
Florent Aide committed
	"text/template"

Florent Aide's avatar
Florent Aide committed
	yaml "gopkg.in/yaml.v2"
Florent Aide's avatar
Florent Aide committed
// Variable ...
type Variable struct {
	Name  string `mapstructure:"name"`
	Value string `mapstructure:"value"`
}

Florent Aide's avatar
Florent Aide committed
// Value ...
type Value struct {
	Key   string `mapstructure:"key"`
	Value string `mapstructure:"value"`
type HelmChart struct {
Florent Aide's avatar
Florent Aide committed
	Type   string      `mapstructure:"type"`
	Name   string      `mapstructure:"name"`
	Values interface{} `mapstructure:"values"`
}

type YttChart struct {
	Type   string  `mapstructure:"type"`
	Name   string  `mapstructure:"name"`
	Values []Value `mapstructure:"values"`
}

type Charts struct {
	Helm map[string]HelmChart `mapstructure:"helm"`
	Ytt  map[string]YttChart  `mapstructure:"ytt"`
}

Florent Aide's avatar
Florent Aide committed
// Spec ...
type Spec struct {
	Variables []Variable `mapstructure:"variables"`
	Charts    Charts     `mapstructure:"charts"`
Florent Aide's avatar
Florent Aide committed
}

// Config is the configuration we get after parsing our beaver.yml file
type Config struct {
	APIVersion string `mapstructure:"apiVersion"`
	Kind       string `mapstructure:"kind"`
	Spec       Spec   `mapstructure:"spec"`
}

Florent Aide's avatar
Florent Aide committed
// NewConfig returns a *Config
steeve.chailloux's avatar
steeve.chailloux committed
func NewConfig(configDir string) (*Config, error) {
	v := viper.New()
	v.SetConfigName("beaver")
	v.AddConfigPath(configDir)
	if err := v.ReadInConfig(); err != nil {
		return nil, err
	}
	var config Config
Florent Aide's avatar
Florent Aide committed
	cfg := &config
steeve.chailloux's avatar
steeve.chailloux committed
	if err := v.Unmarshal(&cfg); err != nil {
Florent Aide's avatar
Florent Aide committed

	return cfg, nil
}

steeve.chailloux's avatar
steeve.chailloux committed
func checkExists(path string) bool {
	_, err := os.Stat(path)
	return !errors.Is(err, os.ErrNotExist)
}

Florent Aide's avatar
Florent Aide committed
// hydrate expands templated variables in our config with concrete values
func (c *Config) hydrate() error {
	if err := c.hydrateHelmCharts(); err != nil {
		return err
	}
	if err := c.hydrateYttCharts(); err != nil {
Florent Aide's avatar
Florent Aide committed
		return err
	}
	return nil
}

func (c *Config) 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
Florent Aide's avatar
Florent Aide committed

func (c *Config) 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()
Florent Aide's avatar
Florent Aide committed
		}
	}
	return nil
}

func (c *Config) hydrateHelmCharts() error {
	for name, chart := range c.Spec.Charts.Helm {
Florent Aide's avatar
Florent Aide committed
		rawChartValues, err := yaml.Marshal(chart.Values)
		if err != nil {
			return fmt.Errorf("failed to get chart values as string: %w", err)
		}
		valueTmpl, err := template.New("chart").Parse(string(rawChartValues))
		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)
		}
		// replace original content with hydrated version
		hydratedChart := chart
		hydratedChart.Values = buf.String()
		c.Spec.Charts.Helm[name] = hydratedChart
Florent Aide's avatar
Florent Aide committed
	}
	return nil
}