feat: Template values files (#216)

Resolves #97
This commit is contained in:
KUOKA Yusuke 2018-08-22 18:53:32 +09:00 committed by GitHub
parent 6c89fac8e5
commit d609dab848
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 32 deletions

View File

@ -476,7 +476,9 @@ func loadDesiredStateFromFile(c *cli.Context, file string) (*state.HelmState, he
namespace := c.GlobalString("namespace")
labels := c.GlobalStringSlice("selector")
st, err := state.ReadFromFile(file)
logger := c.App.Metadata["logger"].(*zap.SugaredLogger)
st, err := state.CreateFromFile(file, logger)
if err != nil {
return nil, nil, false, fmt.Errorf("failed to read %s: %v", file, err)
}
@ -514,7 +516,6 @@ func loadDesiredStateFromFile(c *cli.Context, file string) (*state.HelmState, he
clean(st, errs)
}()
logger := c.App.Metadata["logger"].(*zap.SugaredLogger)
return st, helmexec.New(logger, kubeContext), false, nil
}

View File

@ -19,6 +19,7 @@ import (
"bytes"
"regexp"
"go.uber.org/zap"
yaml "gopkg.in/yaml.v2"
)
@ -32,6 +33,8 @@ type HelmState struct {
Namespace string `yaml:"namespace"`
Repositories []RepositorySpec `yaml:"repositories"`
Releases []ReleaseSpec `yaml:"releases"`
logger *zap.SugaredLogger
}
// HelmSpec to defines helmDefault values
@ -79,28 +82,17 @@ type SetValue struct {
Value string `yaml:"value"`
}
// ReadFromFile loads the helmfile from disk and processes the template
func ReadFromFile(file string) (*HelmState, error) {
content, err := ioutil.ReadFile(file)
// CreateFromFile loads the helmfile from disk and processes the template
func CreateFromFile(file string, logger *zap.SugaredLogger) (*HelmState, error) {
yamlBuf, err := renderTemplateFileToBuffer(file)
if err != nil {
return nil, err
}
tpl, err := stringTemplate().Parse(string(content))
if err != nil {
return nil, err
}
var tplString bytes.Buffer
err = tpl.Execute(&tplString, nil)
if err != nil {
return nil, err
}
return readFromYaml(tplString.Bytes(), file)
return readFromYaml(yamlBuf.Bytes(), file, logger)
}
func readFromYaml(content []byte, file string) (*HelmState, error) {
func readFromYaml(content []byte, file string, logger *zap.SugaredLogger) (*HelmState, error) {
var state HelmState
state.BaseChartPath, _ = filepath.Abs(filepath.Dir(file))
@ -117,6 +109,8 @@ func readFromYaml(content []byte, file string) (*HelmState, error) {
state.DeprecatedReleases = []ReleaseSpec{}
}
state.logger = logger
return &state, nil
}
@ -138,19 +132,36 @@ func getRequiredEnv(name string) (string, error) {
return "", fmt.Errorf("required env var `%s` is not set", name)
}
func renderTemplateString(s string) (string, error) {
func renderTemplateFileToBuffer(file string) (*bytes.Buffer, error) {
content, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
return renderTemplateToBuffer(string(content))
}
func renderTemplateToBuffer(s string) (*bytes.Buffer, error) {
var t, parseErr = stringTemplate().Parse(s)
if parseErr != nil {
return "", parseErr
return nil, parseErr
}
var tplString bytes.Buffer
var execErr = t.Execute(&tplString, nil)
if execErr != nil {
return "", execErr
return nil, execErr
}
return &tplString, nil
}
func renderTemplateString(s string) (string, error) {
tplString, err := renderTemplateToBuffer(s)
if err != nil {
return "", err
}
return tplString.String(), nil
}
@ -194,7 +205,7 @@ func (state *HelmState) SyncReleases(helm helmexec.Interface, additionalValues [
go func() {
for release := range jobQueue {
state.applyDefaultsTo(release)
flags, flagsErr := flagsForRelease(helm, state.BaseChartPath, release)
flags, flagsErr := state.flagsForRelease(helm, state.BaseChartPath, release)
if flagsErr != nil {
errQueue <- flagsErr
doneQueue <- true
@ -274,7 +285,7 @@ func (state *HelmState) DiffReleases(helm helmexec.Interface, additionalValues [
state.applyDefaultsTo(release)
flags, err := flagsForRelease(helm, state.BaseChartPath, release)
flags, err := state.flagsForRelease(helm, state.BaseChartPath, release)
if err != nil {
errs = append(errs, err)
}
@ -358,7 +369,7 @@ func (state *HelmState) LintReleases(helm helmexec.Interface, additionalValues [
go func() {
for release := range jobQueue {
errs := []error{}
flags, err := flagsForRelease(helm, state.BaseChartPath, release)
flags, err := state.flagsForRelease(helm, state.BaseChartPath, release)
if err != nil {
errs = append(errs, err)
}
@ -641,7 +652,7 @@ func chartNameWithoutRepository(chart string) string {
return chartSplit[len(chartSplit)-1]
}
func flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseSpec) ([]string, error) {
func (state *HelmState) flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseSpec) ([]string, error) {
flags := []string{}
if release.Version != "" {
flags = append(flags, "--version", release.Version)
@ -664,7 +675,21 @@ func flagsForRelease(helm helmexec.Interface, basePath string, release *ReleaseS
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
flags = append(flags, "--values", path)
yamlBuf, err := renderTemplateFileToBuffer(path)
if err != nil {
return nil, err
}
valfile, err := ioutil.TempFile("", "values")
if err != nil {
return nil, err
}
defer valfile.Close()
yamlBytes := yamlBuf.Bytes()
if _, err := valfile.Write(yamlBytes); err != nil {
return nil, fmt.Errorf("failed to write %s: %v", valfile.Name(), err)
}
state.logger.Debugf("successfully generated the value file at %s. produced:\n%s", path, string(yamlBytes))
flags = append(flags, "--values", valfile.Name())
case map[interface{}]interface{}:
valfile, err := ioutil.TempFile("", "values")

View File

@ -6,9 +6,12 @@ import (
"testing"
"errors"
"github.com/roboll/helmfile/helmexec"
"strings"
)
var logger = helmexec.NewLogger(os.Stdout, "warn")
func TestReadFromYaml(t *testing.T) {
yamlFile := "example/path/to/yaml/file"
yamlContent := []byte(`releases:
@ -16,7 +19,7 @@ func TestReadFromYaml(t *testing.T) {
namespace: mynamespace
chart: mychart
`)
state, err := readFromYaml(yamlContent, yamlFile)
state, err := readFromYaml(yamlContent, yamlFile, logger)
if err != nil {
t.Errorf("unxpected error: %v", err)
}
@ -39,7 +42,7 @@ func TestReadFromYaml_StrictUnmarshalling(t *testing.T) {
namespace: mynamespace
releases: mychart
`)
_, err := readFromYaml(yamlContent, yamlFile)
_, err := readFromYaml(yamlContent, yamlFile, logger)
if err == nil {
t.Error("expected an error for wrong key 'releases' which is not in struct")
}
@ -51,7 +54,7 @@ func TestReadFromYaml_DeprecatedReleaseReferences(t *testing.T) {
- name: myrelease
chart: mychart
`)
state, err := readFromYaml(yamlContent, yamlFile)
state, err := readFromYaml(yamlContent, yamlFile, logger)
if err != nil {
t.Errorf("unxpected error: %v", err)
}
@ -73,7 +76,7 @@ releases:
- name: myrelease2
chart: mychart2
`)
_, err := readFromYaml(yamlContent, yamlFile)
_, err := readFromYaml(yamlContent, yamlFile, logger)
if err == nil {
t.Error("expected error")
}
@ -109,7 +112,7 @@ func TestReadFromYaml_FilterReleasesOnLabels(t *testing.T) {
{LabelFilter{positiveLabels: [][]string{[]string{"tier", "frontend"}}, negativeLabels: [][]string{[]string{"foo", "bar"}}},
[]bool{false, true, false}},
}
state, err := readFromYaml(yamlContent, yamlFile)
state, err := readFromYaml(yamlContent, yamlFile, logger)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -148,7 +151,7 @@ func TestReadFromYaml_FilterNegatives(t *testing.T) {
{LabelFilter{negativeLabels: [][]string{[]string{"stage", "pre"}, []string{"stage", "post"}}},
[]bool{false, false, true}},
}
state, err := readFromYaml(yamlContent, yamlFile)
state, err := readFromYaml(yamlContent, yamlFile, logger)
if err != nil {
t.Errorf("unexpected error: %v", err)
}