diff --git a/docs/writing-helmfile.md b/docs/writing-helmfile.md new file mode 100644 index 00000000..524fe21c --- /dev/null +++ b/docs/writing-helmfile.md @@ -0,0 +1,72 @@ +# The Helmfile Best Practices Guide + +This guide covers the Helmfile’s considered patterns for writing advanced helmfiles. It focuses on how helmfile should be structured and executed. + +## Layering + +You may occasionally end up with many helmfiles that shares common parts like which repositories to use, and whichi release to be bundled by default. + +Use Layering to extract te common parts into a dedicated *library helmfile*s, so that each helmfile becomes DRY. + +Let's assume that your `helmfile.yaml` looks like: + +``` +{ readFile "commons.yaml" }} +--- +{{ readFile "environments.yaml" }} +--- +releases: +- name: myapp + chart: mychart +``` + +Whereas `commons.yaml` contained a monitoring agent: + +```yaml +releases: +- name: metricbaet + chart: stable/metricbeat +``` + +And `environments.yaml` contained well-known environments: + +```yaml +environments: + development: + production: +``` + +At run time, template expressions in your `helmfile.yaml` are executed: + +```yaml +releases: +- name: metricbaet + chart: stable/metricbeat +--- +environments: + development: + production: +--- +releases: +- name: myapp + chart: mychart +``` + +Resulting YAML documents are merged in the order of occurrence, +so that your `helmfile.yaml` becomes: + +```yaml +environments: + development: + production: + +releases: +- name: metricbaet + chart: stable/metricbeat +- name: myapp + chart: mychart +``` + +Great! + +Now, repeat the above steps for each your `helmfile.yaml`, so that all your helmfiles becomes DRY. diff --git a/state/create.go b/state/create.go index 9a5b7fbb..dd9bcb00 100644 --- a/state/create.go +++ b/state/create.go @@ -1,6 +1,7 @@ package state import ( + "bytes" "fmt" "github.com/imdario/mergo" "github.com/roboll/helmfile/environment" @@ -8,6 +9,7 @@ import ( "github.com/roboll/helmfile/valuesfile" "go.uber.org/zap" "gopkg.in/yaml.v2" + "io" "io/ioutil" "os" "path/filepath" @@ -64,16 +66,32 @@ func (c *creator) CreateFromYaml(content []byte, file string, env string) (*Helm if err != nil { return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err} } + state.FilePath = file state.basePath = basePath - unmarshal := yaml.UnmarshalStrict + decoder := yaml.NewDecoder(bytes.NewReader(content)) if !c.Strict { - unmarshal = yaml.Unmarshal + decoder.SetStrict(false) + } else { + decoder.SetStrict(true) } - if err := unmarshal(content, &state); err != nil { - return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err} + i := 0 + for { + i++ + + var intermediate HelmState + + err := decoder.Decode(&intermediate) + if err == io.EOF { + break + } else if err != nil { + return nil, &StateLoadError{fmt.Sprintf("failed to read %s: reading document at index %d", file, i), err} + } + + if err := mergo.Merge(&state, &intermediate, mergo.WithAppendSlice); err != nil { + return nil, &StateLoadError{fmt.Sprintf("failed to read %s: merging document at index %d", file, i), err} + } } - state.FilePath = file if len(state.DeprecatedReleases) > 0 { if len(state.Releases) > 0 {