feat(environment): Allow missing environment in helmfile.yaml, partly. (#294)

helmfile as of today ensures that all the targeted helmfile.yaml to have the specified environment defined in it.
That is, `helmfile --environment prod -f helmfile.d/ sync` fails if any helmfile under `helmfile.d/` is missing the `production` environment.

This changes the validation logic, so that helmfile fails only when all the helmfiles miss the environment.

Resolves #279
This commit is contained in:
KUOKA Yusuke 2018-09-04 22:04:37 +09:00 committed by GitHub
parent 7d7ca74a05
commit 1c3bfcca10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 17 deletions

44
main.go
View File

@ -539,14 +539,15 @@ func findAndIterateOverDesiredStates(fileOrDir string, converge func(*state.Helm
if err != nil {
return err
}
allSelectorNotMatched := true
noTargetFoundForAllHelmfiles := true
for _, f := range desiredStateFiles {
logger.Debugf("Processing %s", f)
yamlBuf, err := tmpl.NewFileRenderer(ioutil.ReadFile, "", environment.EmptyEnvironment).RenderTemplateFileToBuffer(f)
if err != nil {
return err
}
state, helm, noReleases, err := loadDesiredStateFromFile(
st, helm, noReleasesMatchingSelector, err := loadDesiredStateFromFile(
yamlBuf.Bytes(),
f,
kubeContext,
@ -555,12 +556,21 @@ func findAndIterateOverDesiredStates(fileOrDir string, converge func(*state.Helm
env,
logger,
)
if err != nil {
return err
}
if len(state.Helmfiles) > 0 {
for _, globPattern := range state.Helmfiles {
var noTarget bool
if err != nil {
switch stateLoadErr := err.(type) {
// Addresses https://github.com/roboll/helmfile/issues/279
case *state.StateLoadError:
switch stateLoadErr.Cause.(type) {
case *state.UndefinedEnvError:
noTarget = true
}
default:
return err
}
} else if len(st.Helmfiles) > 0 {
for _, globPattern := range st.Helmfiles {
matches, err := filepath.Glob(globPattern)
if err != nil {
return fmt.Errorf("failed processing %s: %v", globPattern, err)
@ -573,19 +583,25 @@ func findAndIterateOverDesiredStates(fileOrDir string, converge func(*state.Helm
}
}
return nil
} else {
noTarget = noReleasesMatchingSelector
}
allSelectorNotMatched = allSelectorNotMatched && noReleases
if noReleases {
noTargetFoundForAllHelmfiles = noTargetFoundForAllHelmfiles && noTarget
if noTarget {
continue
}
errs := converge(state, helm)
if err := clean(state, errs); err != nil {
errs := converge(st, helm)
if err := clean(st, errs); err != nil {
return err
}
}
if allSelectorNotMatched {
logger.Error("specified selector did not match any releases in any helmfile")
if noTargetFoundForAllHelmfiles {
logger.Errorf(
"err: no releases found that matches specified selector(%s) and environment(%s), in any helmfile",
strings.Join(selectors, ", "),
env,
)
os.Exit(2)
}
return nil
@ -651,7 +667,7 @@ func directoryExistsAt(path string) bool {
func loadDesiredStateFromFile(yaml []byte, file string, kubeContext, namespace string, labels []string, env string, logger *zap.SugaredLogger) (*state.HelmState, helmexec.Interface, bool, error) {
st, err := state.CreateFromYaml(yaml, file, env, logger)
if err != nil {
return nil, nil, false, fmt.Errorf("failed to read %s: %v", file, err)
return nil, nil, false, err
}
if st.Context != "" {

View File

@ -13,6 +13,23 @@ import (
"path/filepath"
)
type StateLoadError struct {
msg string
Cause error
}
func (e *StateLoadError) Error() string {
return fmt.Sprintf("%s: %v", e.msg, e.Cause)
}
type UndefinedEnvError struct {
msg string
}
func (e *UndefinedEnvError) Error() string {
return e.msg
}
func CreateFromYaml(content []byte, file string, env string, logger *zap.SugaredLogger) (*HelmState, error) {
return createFromYamlWithFileReader(content, file, env, logger, ioutil.ReadFile)
}
@ -22,7 +39,7 @@ func createFromYamlWithFileReader(content []byte, file string, env string, logge
state.basePath, _ = filepath.Abs(filepath.Dir(file))
if err := yaml.UnmarshalStrict(content, &state); err != nil {
return nil, err
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err}
}
state.FilePath = file
@ -38,7 +55,7 @@ func createFromYamlWithFileReader(content []byte, file string, env string, logge
e, err := state.loadEnv(env, readFile)
if err != nil {
return nil, err
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err}
}
state.env = *e
@ -92,7 +109,7 @@ func (state *HelmState) loadEnv(name string, readFile func(string) ([]byte, erro
}
}
} else if name != DefaultEnv {
return nil, fmt.Errorf("environment \"%s\" is not defined in \"%s\"", name, state.FilePath)
return nil, &UndefinedEnvError{msg: fmt.Sprintf("environment \"%s\" is not defined", name)}
}
return &environment.Environment{Name: name, Values: envVals}, nil