Fix terminology (#35)
In a backward compatible manner, so that (I believe) we could move the discussion made in #25 forward. Fixes #25
This commit is contained in:
parent
efdede1658
commit
0fc74ea771
16
README.md
16
README.md
|
|
@ -14,7 +14,7 @@ Helmfile is a declarative spec for deploying helm charts. It lets you...
|
|||
|
||||
To avoid upgrades for each iteration of `helm`, the `helmfile` executable delegates to `helm` - as a result, `helm` must be installed.
|
||||
|
||||
The default helmfile is `charts.yaml`:
|
||||
The default helmfile is `helmfile.yaml`:
|
||||
|
||||
```
|
||||
repositories:
|
||||
|
|
@ -23,11 +23,11 @@ repositories:
|
|||
|
||||
context: kube-context # kube-context (--kube-context)
|
||||
|
||||
charts:
|
||||
releases:
|
||||
# Published chart example
|
||||
- name: vault # helm deployment name
|
||||
- name: vault # name of this release
|
||||
namespace: vault # target namespace
|
||||
chart: roboll/vault-secret-manager # chart reference (repository)
|
||||
chart: roboll/vault-secret-manager # the chart being installed to create this release, referenced by `repository/chart` syntax
|
||||
values: [ vault.yaml ] # value files (--values)
|
||||
set: # values (--set)
|
||||
- name: address
|
||||
|
|
@ -38,9 +38,9 @@ charts:
|
|||
value: "{{ env \"PLATFORM_ID\" }}.my-domain.com" # Interpolate environment variable with a fixed string
|
||||
|
||||
# Local chart example
|
||||
- name: grafana # helm deployment name
|
||||
- name: grafana # name of this release
|
||||
namespace: another # target namespace
|
||||
chart: ../my-charts/grafana # chart reference (relative path to manifest)
|
||||
chart: ../my-charts/grafana # the chart being installed to create this release, referenced by relative path to local chart
|
||||
values:
|
||||
- "../../my-values/grafana/values.yaml" # Values file (relative path to manifest)
|
||||
- "./values/{{ env \"PLATFORM_ENV\" }}/config.yaml" # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment.
|
||||
|
|
@ -59,7 +59,7 @@ NAME:
|
|||
helmfile -
|
||||
|
||||
USAGE:
|
||||
main [global options] command [command options] [arguments...]
|
||||
helmfile [global options] command [command options] [arguments...]
|
||||
|
||||
COMMANDS:
|
||||
repos sync repositories from state file (helm repo add && helm repo update)
|
||||
|
|
@ -69,7 +69,7 @@ COMMANDS:
|
|||
delete delete charts from state file (helm delete)
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--file FILE, -f FILE load config from FILE (default: "charts.yaml")
|
||||
--file FILE, -f FILE load config from FILE (default: "helmfile.yaml")
|
||||
--quiet, -q silence output
|
||||
--kube-context value Set kubectl context. Uses current context by default
|
||||
--help, -h show help
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
charts:
|
||||
# Published chart example
|
||||
- name: grafana # helm deployment name
|
||||
- name: grafana # helm release name
|
||||
namespace: grafana # target namespace
|
||||
chart: stable/grafana # chart reference (repository)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func (helm *execer) UpdateRepo() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (helm *execer) SyncChart(name, chart string, flags ...string) error {
|
||||
func (helm *execer) SyncRelease(name, chart string, flags ...string) error {
|
||||
out, err := helm.exec(append([]string{"upgrade", "--install", name, chart}, flags...)...)
|
||||
if helm.writer != nil {
|
||||
helm.writer.Write(out)
|
||||
|
|
@ -54,7 +54,7 @@ func (helm *execer) SyncChart(name, chart string, flags ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (helm *execer) DiffChart(name, chart string, flags ...string) error {
|
||||
func (helm *execer) DiffRelease(name, chart string, flags ...string) error {
|
||||
out, err := helm.exec(append([]string{"diff", name, chart}, flags...)...)
|
||||
if helm.writer != nil {
|
||||
helm.writer.Write(out)
|
||||
|
|
@ -62,7 +62,7 @@ func (helm *execer) DiffChart(name, chart string, flags ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (helm *execer) DeleteChart(name string) error {
|
||||
func (helm *execer) DeleteRelease(name string) error {
|
||||
out, err := helm.exec("delete", "--purge", name)
|
||||
if helm.writer != nil {
|
||||
helm.writer.Write(out)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ type Interface interface {
|
|||
AddRepo(name, repository string) error
|
||||
UpdateRepo() error
|
||||
|
||||
SyncChart(name, chart string, flags ...string) error
|
||||
DiffChart(name, chart string, flags ...string) error
|
||||
DeleteChart(name string) error
|
||||
SyncRelease(name, chart string, flags ...string) error
|
||||
DiffRelease(name, chart string, flags ...string) error
|
||||
DeleteRelease(name string) error
|
||||
}
|
||||
|
|
|
|||
33
main.go
33
main.go
|
|
@ -13,7 +13,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
helmfile = "charts.yaml"
|
||||
DefaultHelmfile = "helmfile.yaml"
|
||||
DeprecatedHelmfile = "charts.yaml"
|
||||
)
|
||||
|
||||
var Version string
|
||||
|
|
@ -27,7 +28,7 @@ func main() {
|
|||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "file, f",
|
||||
Value: helmfile,
|
||||
Value: DefaultHelmfile,
|
||||
Usage: "load config from `FILE`",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
|
|
@ -108,7 +109,7 @@ func main() {
|
|||
values := c.StringSlice("values")
|
||||
workers := c.Int("concurrency")
|
||||
|
||||
if errs := state.SyncCharts(helm, values, workers); errs != nil && len(errs) > 0 {
|
||||
if errs := state.SyncReleases(helm, values, workers); errs != nil && len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
fmt.Printf("err: %s\n", err.Error())
|
||||
}
|
||||
|
|
@ -157,7 +158,7 @@ func main() {
|
|||
|
||||
values := c.StringSlice("values")
|
||||
|
||||
if errs := state.DiffCharts(helm, values); errs != nil && len(errs) > 0 {
|
||||
if errs := state.DiffReleases(helm, values); errs != nil && len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
fmt.Printf("err: %s\n", err.Error())
|
||||
}
|
||||
|
|
@ -196,7 +197,7 @@ func main() {
|
|||
values := c.StringSlice("values")
|
||||
workers := c.Int("concurrency")
|
||||
|
||||
if errs := state.SyncCharts(helm, values, workers); errs != nil && len(errs) > 0 {
|
||||
if errs := state.SyncReleases(helm, values, workers); errs != nil && len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
fmt.Printf("err: %s\n", err.Error())
|
||||
}
|
||||
|
|
@ -214,7 +215,7 @@ func main() {
|
|||
return err
|
||||
}
|
||||
|
||||
if errs := state.DeleteCharts(helm); errs != nil && len(errs) > 0 {
|
||||
if errs := state.DeleteReleases(helm); errs != nil && len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
fmt.Printf("err: %s\n", err.Error())
|
||||
}
|
||||
|
|
@ -238,28 +239,36 @@ func before(c *cli.Context) (*state.HelmState, helmexec.Interface, error) {
|
|||
kubeContext := c.GlobalString("kube-context")
|
||||
namespace := c.GlobalString("namespace")
|
||||
|
||||
state, err := state.ReadFromFile(file)
|
||||
st, err := state.ReadFromFile(file)
|
||||
if err != nil && strings.Contains(err.Error(), fmt.Sprintf("open %s:", DefaultHelmfile)) {
|
||||
var fallbackErr error
|
||||
st, fallbackErr = state.ReadFromFile(DeprecatedHelmfile)
|
||||
if fallbackErr != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read %s and %s: %v", file, DeprecatedHelmfile, err)
|
||||
}
|
||||
log.Printf("warn: charts.yaml is loaded: charts.yaml is deprecated in favor of helmfile.yaml. See https://github.com/roboll/helmfile/issues/25 for more information")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if state.Context != "" {
|
||||
if st.Context != "" {
|
||||
if kubeContext != "" {
|
||||
log.Printf("err: Cannot use option --kube-context and set attribute context.")
|
||||
os.Exit(1)
|
||||
}
|
||||
kubeContext = state.Context
|
||||
kubeContext = st.Context
|
||||
}
|
||||
if namespace != "" {
|
||||
if state.Namespace != "" {
|
||||
if st.Namespace != "" {
|
||||
log.Printf("err: Cannot use option --namespace and set attribute namespace.")
|
||||
os.Exit(1)
|
||||
}
|
||||
state.Namespace = namespace
|
||||
st.Namespace = namespace
|
||||
}
|
||||
var writer io.Writer
|
||||
if !quiet {
|
||||
writer = os.Stdout
|
||||
}
|
||||
|
||||
return state, helmexec.NewHelmExec(writer, kubeContext), nil
|
||||
return st, helmexec.NewHelmExec(writer, kubeContext), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,17 +13,19 @@ import (
|
|||
"github.com/roboll/helmfile/helmexec"
|
||||
|
||||
"bytes"
|
||||
yaml "gopkg.in/yaml.v1"
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
yaml "gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
type HelmState struct {
|
||||
BaseChartPath string
|
||||
Context string `yaml:"context"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
Repositories []RepositorySpec `yaml:"repositories"`
|
||||
Charts []ChartSpec `yaml:"charts"`
|
||||
BaseChartPath string
|
||||
Context string `yaml:"context"`
|
||||
DeprecatedReleases []ReleaseSpec `yaml:"charts"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
Repositories []RepositorySpec `yaml:"repositories"`
|
||||
Releases []ReleaseSpec `yaml:"releases"`
|
||||
}
|
||||
|
||||
type RepositorySpec struct {
|
||||
|
|
@ -31,11 +33,13 @@ type RepositorySpec struct {
|
|||
URL string `yaml:"url"`
|
||||
}
|
||||
|
||||
type ChartSpec struct {
|
||||
type ReleaseSpec struct {
|
||||
// Chart is the name of the chart being installed to create this release
|
||||
Chart string `yaml:"chart"`
|
||||
Version string `yaml:"version"`
|
||||
Verify bool `yaml:"verify"`
|
||||
|
||||
// Name is the name of this release
|
||||
Name string `yaml:"name"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
Values []string `yaml:"values"`
|
||||
|
|
@ -55,13 +59,25 @@ func ReadFromFile(file string) (*HelmState, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readFromYaml(content, file)
|
||||
}
|
||||
|
||||
func readFromYaml(content []byte, file string) (*HelmState, error) {
|
||||
var state HelmState
|
||||
|
||||
state.BaseChartPath, _ = filepath.Abs(path.Dir(file))
|
||||
if err := yaml.Unmarshal(content, &state); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(state.DeprecatedReleases) > 0 {
|
||||
if len(state.Releases) > 0 {
|
||||
return nil, fmt.Errorf("failed to parse %s: you can't specify both `charts` and `releases` sections", file)
|
||||
}
|
||||
state.Releases = state.DeprecatedReleases
|
||||
state.DeprecatedReleases = []ReleaseSpec{}
|
||||
}
|
||||
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +116,7 @@ func renderTemplateString(s string) (string, error) {
|
|||
return tplString.String(), nil
|
||||
}
|
||||
|
||||
func (state *HelmState) applyDefaultsTo(spec ChartSpec) ChartSpec {
|
||||
func (state *HelmState) applyDefaultsTo(spec ReleaseSpec) ReleaseSpec {
|
||||
spec.Namespace = state.Namespace
|
||||
return spec
|
||||
}
|
||||
|
|
@ -130,21 +146,21 @@ func (state *HelmState) SyncRepos(helm helmexec.Interface) []error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (state *HelmState) SyncCharts(helm helmexec.Interface, additonalValues []string, workerLimit int) []error {
|
||||
func (state *HelmState) SyncReleases(helm helmexec.Interface, additonalValues []string, workerLimit int) []error {
|
||||
errs := []error{}
|
||||
jobQueue := make(chan ChartSpec)
|
||||
jobQueue := make(chan ReleaseSpec)
|
||||
doneQueue := make(chan bool)
|
||||
errQueue := make(chan error)
|
||||
|
||||
if workerLimit < 1 {
|
||||
workerLimit = len(state.Charts)
|
||||
workerLimit = len(state.Releases)
|
||||
}
|
||||
|
||||
for w := 1; w <= workerLimit; w++ {
|
||||
go func() {
|
||||
for chart := range jobQueue {
|
||||
chartWithDefaults := state.applyDefaultsTo(chart)
|
||||
flags, flagsErr := flagsForChart(state.BaseChartPath, &chartWithDefaults)
|
||||
for release := range jobQueue {
|
||||
releaseWithDefaults := state.applyDefaultsTo(release)
|
||||
flags, flagsErr := flagsForRelease(state.BaseChartPath, &releaseWithDefaults)
|
||||
if flagsErr != nil {
|
||||
errQueue <- flagsErr
|
||||
doneQueue <- true
|
||||
|
|
@ -166,7 +182,7 @@ func (state *HelmState) SyncCharts(helm helmexec.Interface, additonalValues []st
|
|||
continue
|
||||
}
|
||||
|
||||
if err := helm.SyncChart(chart.Name, normalizeChart(state.BaseChartPath, chart.Chart), flags...); err != nil {
|
||||
if err := helm.SyncRelease(release.Name, normalizeChart(state.BaseChartPath, release.Chart), flags...); err != nil {
|
||||
errQueue <- err
|
||||
}
|
||||
doneQueue <- true
|
||||
|
|
@ -175,13 +191,13 @@ func (state *HelmState) SyncCharts(helm helmexec.Interface, additonalValues []st
|
|||
}
|
||||
|
||||
go func() {
|
||||
for _, chart := range state.Charts {
|
||||
jobQueue <- chart
|
||||
for _, release := range state.Releases {
|
||||
jobQueue <- release
|
||||
}
|
||||
close(jobQueue)
|
||||
}()
|
||||
|
||||
for i := 0; i < len(state.Charts); {
|
||||
for i := 0; i < len(state.Releases); {
|
||||
select {
|
||||
case err := <-errQueue:
|
||||
errs = append(errs, err)
|
||||
|
|
@ -197,16 +213,16 @@ func (state *HelmState) SyncCharts(helm helmexec.Interface, additonalValues []st
|
|||
return nil
|
||||
}
|
||||
|
||||
func (state *HelmState) DiffCharts(helm helmexec.Interface, additonalValues []string) []error {
|
||||
func (state *HelmState) DiffReleases(helm helmexec.Interface, additonalValues []string) []error {
|
||||
var wg sync.WaitGroup
|
||||
errs := []error{}
|
||||
|
||||
for _, chart := range state.Charts {
|
||||
for _, release := range state.Releases {
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup, chart ChartSpec) {
|
||||
go func(wg *sync.WaitGroup, release ReleaseSpec) {
|
||||
// Plugin command doesn't support explicit namespace
|
||||
chart.Namespace = ""
|
||||
flags, flagsErr := flagsForChart(state.BaseChartPath, &chart)
|
||||
release.Namespace = ""
|
||||
flags, flagsErr := flagsForRelease(state.BaseChartPath, &release)
|
||||
if flagsErr != nil {
|
||||
errs = append(errs, flagsErr)
|
||||
}
|
||||
|
|
@ -218,12 +234,12 @@ func (state *HelmState) DiffCharts(helm helmexec.Interface, additonalValues []st
|
|||
flags = append(flags, "--values", valfile)
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
if err := helm.DiffChart(chart.Name, normalizeChart(state.BaseChartPath, chart.Chart), flags...); err != nil {
|
||||
if err := helm.DiffRelease(release.Name, normalizeChart(state.BaseChartPath, release.Chart), flags...); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(&wg, chart)
|
||||
}(&wg, release)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
|
|
@ -234,18 +250,18 @@ func (state *HelmState) DiffCharts(helm helmexec.Interface, additonalValues []st
|
|||
return nil
|
||||
}
|
||||
|
||||
func (state *HelmState) DeleteCharts(helm helmexec.Interface) []error {
|
||||
func (state *HelmState) DeleteReleases(helm helmexec.Interface) []error {
|
||||
var wg sync.WaitGroup
|
||||
errs := []error{}
|
||||
|
||||
for _, chart := range state.Charts {
|
||||
for _, release := range state.Releases {
|
||||
wg.Add(1)
|
||||
go func(wg *sync.WaitGroup, chart ChartSpec) {
|
||||
if err := helm.DeleteChart(chart.Name); err != nil {
|
||||
go func(wg *sync.WaitGroup, release ReleaseSpec) {
|
||||
if err := helm.DeleteRelease(release.Name); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
wg.Done()
|
||||
}(&wg, chart)
|
||||
}(&wg, release)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
|
|
@ -268,18 +284,18 @@ func normalizeChart(basePath, chart string) string {
|
|||
return filepath.Join(basePath, chart)
|
||||
}
|
||||
|
||||
func flagsForChart(basePath string, chart *ChartSpec) ([]string, error) {
|
||||
func flagsForRelease(basePath string, release *ReleaseSpec) ([]string, error) {
|
||||
flags := []string{}
|
||||
if chart.Version != "" {
|
||||
flags = append(flags, "--version", chart.Version)
|
||||
if release.Version != "" {
|
||||
flags = append(flags, "--version", release.Version)
|
||||
}
|
||||
if chart.Verify {
|
||||
if release.Verify {
|
||||
flags = append(flags, "--verify")
|
||||
}
|
||||
if chart.Namespace != "" {
|
||||
flags = append(flags, "--namespace", chart.Namespace)
|
||||
if release.Namespace != "" {
|
||||
flags = append(flags, "--namespace", release.Namespace)
|
||||
}
|
||||
for _, value := range chart.Values {
|
||||
for _, value := range release.Values {
|
||||
valfile := filepath.Join(basePath, value)
|
||||
valfileRendered, err := renderTemplateString(valfile)
|
||||
if err != nil {
|
||||
|
|
@ -287,9 +303,9 @@ func flagsForChart(basePath string, chart *ChartSpec) ([]string, error) {
|
|||
}
|
||||
flags = append(flags, "--values", valfileRendered)
|
||||
}
|
||||
if len(chart.SetValues) > 0 {
|
||||
if len(release.SetValues) > 0 {
|
||||
val := []string{}
|
||||
for _, set := range chart.SetValues {
|
||||
for _, set := range release.SetValues {
|
||||
renderedValue, err := renderTemplateString(set.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -303,10 +319,10 @@ func flagsForChart(basePath string, chart *ChartSpec) ([]string, error) {
|
|||
* START 'env' section for backwards compatibility
|
||||
***********/
|
||||
// The 'env' section is not really necessary any longer, as 'set' would now provide the same functionality
|
||||
if len(chart.EnvValues) > 0 {
|
||||
if len(release.EnvValues) > 0 {
|
||||
val := []string{}
|
||||
envValErrs := []string{}
|
||||
for _, set := range chart.EnvValues {
|
||||
for _, set := range release.EnvValues {
|
||||
value, isSet := os.LookupEnv(set.Value)
|
||||
if isSet {
|
||||
val = append(val, fmt.Sprintf("%s=%s", set.Name, value))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadFromYaml(t *testing.T) {
|
||||
yamlFile := "example/path/to/yaml/file"
|
||||
yamlContent := []byte(`releases:
|
||||
- name: myrelease
|
||||
chart: mychart
|
||||
`)
|
||||
state, err := readFromYaml(yamlContent, yamlFile)
|
||||
if err != nil {
|
||||
t.Errorf("unxpected error: %v", err)
|
||||
}
|
||||
|
||||
if state.Releases[0].Name != "myrelease" {
|
||||
t.Errorf("unexpected release name: expected=myrelease actual=%s", state.Releases[0].Name)
|
||||
}
|
||||
if state.Releases[0].Chart != "mychart" {
|
||||
t.Errorf("unexpected chart name: expected=mychart actual=%s", state.Releases[0].Chart)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFromYaml_DeprecatedReleaseReferences(t *testing.T) {
|
||||
yamlFile := "example/path/to/yaml/file"
|
||||
yamlContent := []byte(`charts:
|
||||
- name: myrelease
|
||||
chart: mychart
|
||||
`)
|
||||
state, err := readFromYaml(yamlContent, yamlFile)
|
||||
if err != nil {
|
||||
t.Errorf("unxpected error: %v", err)
|
||||
}
|
||||
|
||||
if state.Releases[0].Name != "myrelease" {
|
||||
t.Errorf("unexpected release name: expected=myrelease actual=%s", state.Releases[0].Name)
|
||||
}
|
||||
if state.Releases[0].Chart != "mychart" {
|
||||
t.Errorf("unexpected chart name: expected=mychart actual=%s", state.Releases[0].Chart)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFromYaml_ConflictingReleasesConfig(t *testing.T) {
|
||||
yamlFile := "example/path/to/yaml/file"
|
||||
yamlContent := []byte(`charts:
|
||||
- name: myrelease1
|
||||
chart: mychart1
|
||||
releases:
|
||||
- name: myrelease2
|
||||
chart: mychart2
|
||||
`)
|
||||
_, err := readFromYaml(yamlContent, yamlFile)
|
||||
if err == nil {
|
||||
t.Error("expected error")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue