feat: Merge multiple docs inside a single helmfile.yaml (#270)
Resolves #254
This commit is contained in:
parent
1ade353c1a
commit
9808849986
|
|
@ -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.
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/roboll/helmfile/environment"
|
"github.com/roboll/helmfile/environment"
|
||||||
|
|
@ -8,6 +9,7 @@ import (
|
||||||
"github.com/roboll/helmfile/valuesfile"
|
"github.com/roboll/helmfile/valuesfile"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -64,16 +66,32 @@ func (c *creator) CreateFromYaml(content []byte, file string, env string) (*Helm
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err}
|
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err}
|
||||||
}
|
}
|
||||||
|
state.FilePath = file
|
||||||
state.basePath = basePath
|
state.basePath = basePath
|
||||||
|
|
||||||
unmarshal := yaml.UnmarshalStrict
|
decoder := yaml.NewDecoder(bytes.NewReader(content))
|
||||||
if !c.Strict {
|
if !c.Strict {
|
||||||
unmarshal = yaml.Unmarshal
|
decoder.SetStrict(false)
|
||||||
|
} else {
|
||||||
|
decoder.SetStrict(true)
|
||||||
}
|
}
|
||||||
if err := unmarshal(content, &state); err != nil {
|
i := 0
|
||||||
return nil, &StateLoadError{fmt.Sprintf("failed to read %s", file), err}
|
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.DeprecatedReleases) > 0 {
|
||||||
if len(state.Releases) > 0 {
|
if len(state.Releases) > 0 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue