Do not fail due to missing env in base helmfile (#1009)
When helmfile is run with `--environment NAME` and there was a base hemlfile that misses `environments`, helmfile had been trying to load env values for NAME and failing. A base helmfile is allowed to reference values from within itself, but that's optional. In other words, a base helmfile that misses the env is okay as long as it doesn't self-reference env values. So, this change allows missing env and env values while loading base helmfile. After loading, a base helmfile can fail due to referencing missing env values, but that's okay. Fixes #1008
This commit is contained in:
parent
27c098c17c
commit
07c42474cc
|
|
@ -177,6 +177,42 @@ releases:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) {
|
||||||
|
files := map[string]string{
|
||||||
|
"/path/to/base.yaml": `
|
||||||
|
helmDefaults:
|
||||||
|
wait: true
|
||||||
|
`,
|
||||||
|
"/path/to/helmfile.yaml": `
|
||||||
|
bases:
|
||||||
|
- base.yaml
|
||||||
|
environments:
|
||||||
|
test:
|
||||||
|
releases:
|
||||||
|
- name: zipkin
|
||||||
|
chart: stable/zipkin
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
fs := testhelper.NewTestFs(files)
|
||||||
|
app := &App{
|
||||||
|
KubeContext: "default",
|
||||||
|
Logger: helmexec.NewLogger(os.Stderr, "debug"),
|
||||||
|
Namespace: "",
|
||||||
|
Env: "test",
|
||||||
|
}
|
||||||
|
app = injectFs(app, fs)
|
||||||
|
noop := func(st *state.HelmState, helm helmexec.Interface) []error {
|
||||||
|
return []error{}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.VisitDesiredStatesWithReleasesFiltered(
|
||||||
|
"helmfile.yaml", noop,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVisitDesiredStatesWithReleasesFiltered_MissingEnvValuesFileHandler(t *testing.T) {
|
func TestVisitDesiredStatesWithReleasesFiltered_MissingEnvValuesFileHandler(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,10 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadEnvValues loads environment values files relative to the `baseDir`
|
// LoadEnvValues loads environment values files relative to the `baseDir`
|
||||||
func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *environment.Environment) (*HelmState, error) {
|
func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *environment.Environment, failOnMissingEnv bool) (*HelmState, error) {
|
||||||
state := *target
|
state := *target
|
||||||
|
|
||||||
e, err := state.loadEnvValues(env, ctxEnv, c.readFile, c.glob)
|
e, err := state.loadEnvValues(env, failOnMissingEnv, ctxEnv, c.readFile, c.glob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
|
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +135,7 @@ func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *envi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses YAML into HelmState, while loading environment values files relative to the `baseDir`
|
// Parses YAML into HelmState, while loading environment values files relative to the `baseDir`
|
||||||
|
// evaluateBases=true means that this is NOT a base helmfile
|
||||||
func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envName string, evaluateBases bool, envValues *environment.Environment) (*HelmState, error) {
|
func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envName string, evaluateBases bool, envValues *environment.Environment) (*HelmState, error) {
|
||||||
state, err := c.Parse(content, baseDir, file)
|
state, err := c.Parse(content, baseDir, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -145,14 +146,14 @@ func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envNam
|
||||||
if len(state.Bases) > 0 {
|
if len(state.Bases) > 0 {
|
||||||
return nil, errors.New("nested `base` helmfile is unsupported. please submit a feature request if you need this!")
|
return nil, errors.New("nested `base` helmfile is unsupported. please submit a feature request if you need this!")
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
state, err = c.loadBases(envValues, state, baseDir)
|
state, err = c.loadBases(envValues, state, baseDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state, err = c.LoadEnvValues(state, envName, envValues)
|
state, err = c.LoadEnvValues(state, envName, envValues, evaluateBases)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +183,7 @@ func (c *StateCreator) loadBases(envValues *environment.Environment, st *HelmSta
|
||||||
return layers[0], nil
|
return layers[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment, readFile func(string) ([]byte, error), glob func(string) ([]string, error)) (*environment.Environment, error) {
|
func (st *HelmState) loadEnvValues(name string, failOnMissingEnv bool, ctxEnv *environment.Environment, readFile func(string) ([]byte, error), glob func(string) ([]string, error)) (*environment.Environment, error) {
|
||||||
envVals := map[string]interface{}{}
|
envVals := map[string]interface{}{}
|
||||||
envSpec, ok := st.Environments[name]
|
envSpec, ok := st.Environments[name]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -210,7 +211,7 @@ func (st *HelmState) loadEnvValues(name string, ctxEnv *environment.Environment,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ctxEnv == nil && name != DefaultEnv {
|
} else if ctxEnv == nil && name != DefaultEnv && failOnMissingEnv {
|
||||||
return nil, &UndefinedEnvError{msg: fmt.Sprintf("environment \"%s\" is not defined", name)}
|
return nil, &UndefinedEnvError{msg: fmt.Sprintf("environment \"%s\" is not defined", name)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ func createFromYaml(content []byte, file string, env string, logger *zap.Sugared
|
||||||
abs: filepath.Abs,
|
abs: filepath.Abs,
|
||||||
Strict: true,
|
Strict: true,
|
||||||
}
|
}
|
||||||
return c.ParseAndLoad(content, filepath.Dir(file), file, env, false, nil)
|
return c.ParseAndLoad(content, filepath.Dir(file), file, env, true, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFromYaml(t *testing.T) {
|
func TestReadFromYaml(t *testing.T) {
|
||||||
|
|
@ -108,7 +108,7 @@ bar: {{ readFile "bar.txt" }}
|
||||||
})
|
})
|
||||||
testFs.Cwd = "/example/path/to"
|
testFs.Cwd = "/example/path/to"
|
||||||
|
|
||||||
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, nil, nil).ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", false, nil)
|
state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, nil, nil).ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue