feat: common labels for all releases in a helmfile (#1415)

This adds `comonLabels` option to helmfile by:

- Adding `CommonLabels` to HelmState
- Changing `markExcludedReleases` and `ListReleases` functions to merge common labels into release labels

Resolves #1266
This commit is contained in:
Maksym Lushpenko 2020-08-29 06:14:58 +02:00 committed by GitHub
parent 6b4b76e2bc
commit 5ca7ce15bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 9 deletions

View File

@ -103,6 +103,9 @@ helmDefaults:
# when using helm 3.2+, automatically create release namespaces if they do not exist (default true) # when using helm 3.2+, automatically create release namespaces if they do not exist (default true)
createNamespace: true createNamespace: true
# these labels will be applied to all releases in a Helmfile. Useful in templating if you have a helmfile per environment or customer and don't want to copy the same label to each release
commonLabels:
hello: world
# The desired states of Helm releases. # The desired states of Helm releases.
# #
@ -522,6 +525,41 @@ The `selector` parameter can be specified multiple times. Each parameter is reso
In addition to user supplied labels, the name, the namespace, and the chart are available to be used as selectors. The chart will just be the chart name excluding the repository (Example `stable/filebeat` would be selected using `--selector chart=filebeat`). In addition to user supplied labels, the name, the namespace, and the chart are available to be used as selectors. The chart will just be the chart name excluding the repository (Example `stable/filebeat` would be selected using `--selector chart=filebeat`).
`commonLabels` can be used when you want to apply the same label to all releases and use [templating](##Templates) based on that.
For instance, you install a number of charts on every customer but need to provide different values file per customer.
templates/common.yaml:
```
templates:
nginx: &nginx
name: nginx
chart: stable/nginx-ingress
values:
- ../values/common/{{ .Release.Name }}.yaml
- ../values/{{ .Release.Labels.customer }}/{{ .Release.Name }}.yaml
cert-manager: &cert-manager
name: cert-manager
chart: jetstack/cert-manager
values:
- ../values/common/{{ .Release.Name }}.yaml
- ../values/{{ .Release.Labels.customer }}/{{ .Release.Name }}.yaml
```
helmfile.yaml:
```
{{ readFile "templates/common.yaml" }}
commonLabels:
customer: company
releases:
- <<: *nginx
- <<: *cert-manager
```
## Templates ## Templates
You can use go's text/template expressions in `helmfile.yaml` and `values.yaml.gotmpl` (templated helm values files). `values.yaml` references will be used verbatim. In other words: You can use go's text/template expressions in `helmfile.yaml` and `values.yaml.gotmpl` (templated helm values files). `values.yaml` references will be used verbatim. In other words:

View File

@ -416,6 +416,12 @@ func (a *App) ListReleases(c ListConfigProvider) error {
//var releases m //var releases m
for _, r := range run.state.Releases { for _, r := range run.state.Releases {
labels := "" labels := ""
if r.Labels == nil {
r.Labels = map[string]string{}
}
for k, v := range run.state.CommonLabels {
r.Labels[k] = v
}
for k, v := range r.Labels { for k, v := range r.Labels {
labels = fmt.Sprintf("%s,%s:%s", labels, k, v) labels = fmt.Sprintf("%s,%s:%s", labels, k, v)
} }

View File

@ -4,8 +4,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"github.com/google/go-cmp/cmp"
"github.com/roboll/helmfile/pkg/remote"
"io" "io"
"log" "log"
"os" "os"
@ -17,6 +15,9 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/google/go-cmp/cmp"
"github.com/roboll/helmfile/pkg/remote"
"github.com/roboll/helmfile/pkg/exectest" "github.com/roboll/helmfile/pkg/exectest"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
@ -4051,6 +4052,8 @@ releases:
func TestList(t *testing.T) { func TestList(t *testing.T) {
files := map[string]string{ files := map[string]string{
"/path/to/helmfile.d/first.yaml": ` "/path/to/helmfile.d/first.yaml": `
commonLabels:
common: label
releases: releases:
- name: myrelease1 - name: myrelease1
chart: mychart1 chart: mychart1
@ -4094,11 +4097,11 @@ releases:
assert.NilError(t, err) assert.NilError(t, err)
}) })
expected := `NAME NAMESPACE ENABLED LABELS expected := `NAME NAMESPACE ENABLED LABELS
myrelease1 false id:myrelease1 myrelease1 false common:label,id:myrelease1
myrelease2 true myrelease2 true common:label
myrelease3 true myrelease3 true
myrelease4 true id:myrelease1 myrelease4 true id:myrelease1
` `
assert.Equal(t, expected, out) assert.Equal(t, expected, out)
} }

View File

@ -59,6 +59,7 @@ type HelmState struct {
DeprecatedReleases []ReleaseSpec `yaml:"charts,omitempty"` DeprecatedReleases []ReleaseSpec `yaml:"charts,omitempty"`
OverrideNamespace string `yaml:"namespace,omitempty"` OverrideNamespace string `yaml:"namespace,omitempty"`
Repositories []RepositorySpec `yaml:"repositories,omitempty"` Repositories []RepositorySpec `yaml:"repositories,omitempty"`
CommonLabels map[string]string `yaml:"commonLabels,omitempty"`
Releases []ReleaseSpec `yaml:"releases,omitempty"` Releases []ReleaseSpec `yaml:"releases,omitempty"`
Selectors []string `yaml:"-"` Selectors []string `yaml:"-"`
ApiVersions []string `yaml:"apiVersions,omitempty"` ApiVersions []string `yaml:"apiVersions,omitempty"`
@ -1634,14 +1635,14 @@ func (st *HelmState) SelectReleasesWithOverrides() ([]Release, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, values) rs, err := markExcludedReleases(st.GetReleasesWithOverrides(), st.Selectors, st.CommonLabels, values)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return rs, nil return rs, nil
} }
func markExcludedReleases(releases []ReleaseSpec, selectors []string, values map[string]interface{}) ([]Release, error) { func markExcludedReleases(releases []ReleaseSpec, selectors []string, commonLabels map[string]string, values map[string]interface{}) ([]Release, error) {
var filteredReleases []Release var filteredReleases []Release
filters := []ReleaseFilter{} filters := []ReleaseFilter{}
for _, label := range selectors { for _, label := range selectors {
@ -1661,6 +1662,10 @@ func markExcludedReleases(releases []ReleaseSpec, selectors []string, values map
// Strip off just the last portion for the name stable/newrelic would give newrelic // Strip off just the last portion for the name stable/newrelic would give newrelic
chartSplit := strings.Split(r.Chart, "/") chartSplit := strings.Split(r.Chart, "/")
r.Labels["chart"] = chartSplit[len(chartSplit)-1] r.Labels["chart"] = chartSplit[len(chartSplit)-1]
//Merge CommonLabels into release labels
for k, v := range commonLabels {
r.Labels[k] = v
}
var filterMatch bool var filterMatch bool
for _, f := range filters { for _, f := range filters {
if r.Labels == nil { if r.Labels == nil {

View File

@ -83,6 +83,12 @@ func (st *HelmState) ExecuteTemplates() (*HelmState, error) {
} }
for i, rt := range st.Releases { for i, rt := range st.Releases {
if rt.Labels == nil {
rt.Labels = map[string]string{}
}
for k, v := range st.CommonLabels {
rt.Labels[k] = v
}
successFlag := false successFlag := false
for it, prev := 0, &rt; it < 6; it++ { for it, prev := 0, &rt; it < 6; it++ {
tmplData := st.createReleaseTemplateData(prev, vals) tmplData := st.createReleaseTemplateData(prev, vals)