feat: state values (#647)

This adds `values` to state files as proposed in #640.

```yaml
values:
- key1: val1
- defaults.yaml

environments:
  default:
  - values:
    - environments/default.yaml
  production:
  - values:
    - environments/production.yaml

```

`{{ .Valuese.key1 }}` evaluates to `val1` if and only if it is not overrode via the production or the default env, or command-line args.

Resolves #640
This commit is contained in:
KUOKA Yusuke
2019-06-04 16:34:02 +09:00
committed by GitHub
parent 2d2b3e486f
commit 3710f6233e
11 changed files with 329 additions and 28 deletions
+30 -7
View File
@@ -114,6 +114,12 @@ func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *envi
if err != nil {
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
}
e.Defaults, err = state.loadValuesEntries(nil, state.DefaultValues)
if err != nil {
return nil, err
}
state.Env = *e
return &state, nil
@@ -137,7 +143,12 @@ func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envNam
return nil, err
}
return c.LoadEnvValues(state, envName, envValues)
state, err = c.LoadEnvValues(state, envName, envValues)
if err != nil {
return nil, err
}
return state, nil
}
func (c *StateCreator) loadBases(envValues *environment.Environment, st *HelmState, baseDir string) (*HelmState, error) {
@@ -164,13 +175,8 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
envVals := map[string]interface{}{}
envSpec, ok := st.Environments[name]
if ok {
envValues := append([]interface{}{}, envSpec.Values...)
ld := &EnvironmentValuesLoader{
storage: st.storage(),
readFile: st.readFile,
}
var err error
envVals, err = ld.LoadEnvironmentValues(envSpec.MissingFileHandler, envValues)
envVals, err = st.loadValuesEntries(envSpec.MissingFileHandler, envSpec.Values)
if err != nil {
return nil, err
}
@@ -237,3 +243,20 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
return newEnv, nil
}
func (st *HelmState) loadValuesEntries(missingFileHandler *string, entries []interface{}) (map[string]interface{}, error) {
envVals := map[string]interface{}{}
valuesEntries := append([]interface{}{}, entries...)
ld := &EnvironmentValuesLoader{
storage: st.storage(),
readFile: st.readFile,
}
var err error
envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries)
if err != nil {
return nil, err
}
return envVals, nil
}
+1 -1
View File
@@ -39,7 +39,7 @@ func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *str
}
for _, envvalFullPath := range resolved {
tmplData := EnvironmentTemplateData{Environment: environment.EmptyEnvironment, Namespace: ""}
tmplData := EnvironmentTemplateData{environment.EmptyEnvironment, "", map[string]interface{}{}}
r := tmpl.NewFileRenderer(ld.readFile, filepath.Dir(envvalFullPath), tmplData)
bytes, err := r.RenderToBytes(envvalFullPath)
if err != nil {
+8
View File
@@ -15,6 +15,14 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R
return nil, fmt.Errorf("failed executing template expressions in release \"%s\": %v", r.Name, err)
}
{
ts := result.Name
result.Name, err = renderer.RenderTemplateContentToString([]byte(ts))
if err != nil {
return nil, fmt.Errorf("failed executing template expressions in release \"%s\".name = \"%s\": %v", r.Name, ts, err)
}
}
{
ts := result.Chart
result.Chart, err = renderer.RenderTemplateContentToString([]byte(ts))
+4 -1
View File
@@ -27,6 +27,9 @@ type HelmState struct {
basePath string
FilePath string
// DefaultValues is the default values to be overrode by environment values and command-line overrides
DefaultValues []interface{} `yaml:"values"`
Environments map[string]EnvironmentSpec `yaml:"environments"`
Bases []string `yaml:"bases"`
@@ -1243,7 +1246,7 @@ func (st *HelmState) flagsForLint(helm helmexec.Interface, release *ReleaseSpec,
}
func (st *HelmState) RenderValuesFileToBytes(path string) ([]byte, error) {
r := tmpl.NewFileRenderer(st.readFile, filepath.Dir(path), st.envTemplateData())
r := tmpl.NewFileRenderer(st.readFile, filepath.Dir(path), st.valuesFileTemplateData())
return r.RenderToBytes(path)
}
+39 -4
View File
@@ -2,23 +2,58 @@ package state
import (
"fmt"
"github.com/imdario/mergo"
"github.com/roboll/helmfile/pkg/maputil"
"github.com/roboll/helmfile/pkg/tmpl"
)
func (st *HelmState) envTemplateData() EnvironmentTemplateData {
func (st *HelmState) Values() (map[string]interface{}, error) {
vals := map[string]interface{}{}
if err := mergo.Merge(&vals, st.Env.Defaults, mergo.WithOverride); err != nil {
return nil, err
}
if err := mergo.Merge(&vals, st.Env.Values, mergo.WithOverride); err != nil {
return nil, err
}
vals, err := maputil.CastKeysToStrings(vals)
if err != nil {
return nil, err
}
return vals, nil
}
func (st *HelmState) mustLoadVals() map[string]interface{} {
vals, err := st.Values()
if err != nil {
panic(err)
}
return vals
}
func (st *HelmState) valuesFileTemplateData() EnvironmentTemplateData {
return EnvironmentTemplateData{
st.Env,
st.Namespace,
Environment: st.Env,
Namespace: st.Namespace,
Values: st.mustLoadVals(),
}
}
func (st *HelmState) ExecuteTemplates() (*HelmState, error) {
r := *st
vals, err := st.Values()
if err != nil {
return nil, err
}
for i, rt := range st.Releases {
tmplData := ReleaseTemplateData{
tmplData := releaseTemplateData{
Environment: st.Env,
Release: rt,
Values: vals,
}
renderer := tmpl.NewFileRenderer(st.readFile, st.basePath, tmplData)
r, err := rt.ExecuteTemplateExpressions(renderer)
+6 -2
View File
@@ -15,12 +15,16 @@ type EnvironmentTemplateData struct {
Environment environment.Environment
// Namespace is accessible as `.Namespace` from any non-values template executed by the renderer
Namespace string
// Values is accessible as `.Values` and it contains default state values overrode by environment values and override values.
Values map[string]interface{}
}
// ReleaseTemplateData provides variables accessible while executing golang text/template expressions in releases of a helmfile YAML file
type ReleaseTemplateData struct {
// releaseTemplateData provides variables accessible while executing golang text/template expressions in releases of a helmfile YAML file
type releaseTemplateData struct {
// Environment is accessible as `.Environment` from any template expression executed by the renderer
Environment environment.Environment
// Release is accessible as `.Release` from any template expression executed by the renderer
Release ReleaseSpec
// Values is accessible as `.Values` and it contains default state values overrode by environment values and override values.
Values map[string]interface{}
}