diff --git a/docs/writing-helmfile.md b/docs/writing-helmfile.md index 285c4124e..eb2143bf7 100644 --- a/docs/writing-helmfile.md +++ b/docs/writing-helmfile.md @@ -102,7 +102,7 @@ Release Templating supports the following parts of release definition: - `set` block values: ```yaml # ... - set: + setTemplate: - name: '{{`{{ .Release.Name }}`}}' values: '{{`{{ .Release.Namespace }}`}}' # ... @@ -110,16 +110,16 @@ Release Templating supports the following parts of release definition: - `values` and `secrets` file paths: ```yaml # ... - values: + valuesTemplate: - config/{{`{{ .Release.Name }}`}}/values.yaml - secrets: + secretsTemplate: - config/{{`{{ .Release.Name }}`}}/secrets.yaml # ... ``` - inline `values` map: ```yaml # ... - values: + valuesTemplate: - image: tag: `{{ .Release.Labels.tag }}` # ... diff --git a/pkg/state/release.go b/pkg/state/release.go index 6a954ba37..3ea0c24ea 100644 --- a/pkg/state/release.go +++ b/pkg/state/release.go @@ -93,6 +93,33 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R result.Labels[key] = s.String() } + if result.ValuesTemplate != nil && len(result.ValuesTemplate) > 0 { + for i, t := range result.ValuesTemplate { + switch ts := t.(type) { + case map[interface{}]interface{}: + serialized, err := yaml.Marshal(ts) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, ts, err) + } + + s, err := renderer.RenderTemplateContentToBuffer([]byte(serialized)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, serialized, err) + } + + var deserialized map[interface{}]interface{} + + if err := yaml.Unmarshal(s.Bytes(), &deserialized); err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, ts, err) + } + + result.ValuesTemplate[i] = deserialized + } + } + + result.Values = result.ValuesTemplate + } + for i, t := range result.Values { switch ts := t.(type) { case string: @@ -101,24 +128,6 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%s\": %v", r.Name, i, ts, err) } result.Values[i] = s.String() - case map[interface{}]interface{}: - serialized, err := yaml.Marshal(ts) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, ts, err) - } - - s, err := renderer.RenderTemplateContentToBuffer([]byte(serialized)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, serialized, err) - } - - var deserialized map[interface{}]interface{} - - if err := yaml.Unmarshal(s.Bytes(), &deserialized); err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".values[%d] = \"%v\": %v", r.Name, i, ts, err) - } - - result.Values[i] = deserialized } } @@ -130,42 +139,46 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R result.Secrets[i] = s.String() } - for i, val := range result.SetValues { - { - // name - ts := val.Name - s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].name = \"%s\": %v", r.Name, i, ts, err) + if result.SetValuesTemplate != nil && len(result.SetValuesTemplate) > 0 { + for i, val := range result.SetValuesTemplate { + { + // name + ts := val.Name + s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].name = \"%s\": %v", r.Name, i, ts, err) + } + result.SetValuesTemplate[i].Name = s.String() } - result.SetValues[i].Name = s.String() - } - { - // value - ts := val.Value - s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].value = \"%s\": %v", r.Name, i, ts, err) + { + // value + ts := val.Value + s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].value = \"%s\": %v", r.Name, i, ts, err) + } + result.SetValuesTemplate[i].Value = s.String() } - result.SetValues[i].Value = s.String() - } - { - // file - ts := val.File - s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].file = \"%s\": %v", r.Name, i, ts, err) + { + // file + ts := val.File + s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].file = \"%s\": %v", r.Name, i, ts, err) + } + result.SetValuesTemplate[i].File = s.String() } - result.SetValues[i].File = s.String() - } - for j, ts := range val.Values { - // values - s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) - if err != nil { - return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].values[%d] = \"%s\": %v", r.Name, i, j, ts, err) + for j, ts := range val.Values { + // values + s, err := renderer.RenderTemplateContentToBuffer([]byte(ts)) + if err != nil { + return nil, fmt.Errorf("failed executing template expressions in release \"%s\".set[%d].values[%d] = \"%s\": %v", r.Name, i, j, ts, err) + } + result.SetValuesTemplate[i].Values[j] = s.String() } - result.SetValues[i].Values[j] = s.String() } + + result.SetValues = result.SetValuesTemplate } return result, nil diff --git a/pkg/state/state.go b/pkg/state/state.go index 6a7496260..c6fd53f49 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -153,6 +153,9 @@ type ReleaseSpec struct { Secrets []string `yaml:"secrets,omitempty"` SetValues []SetValue `yaml:"set,omitempty"` + ValuesTemplate []interface{} `yaml:"valuesTemplate,omitempty"` + SetValuesTemplate []SetValue `yaml:"setTemplate,omitempty"` + // The 'env' section is not really necessary any longer, as 'set' would now provide the same functionality EnvValues []SetValue `yaml:"env,omitempty"` diff --git a/pkg/state/state_exec_tmpl_test.go b/pkg/state/state_exec_tmpl_test.go index 504caadfd..d5a8035f8 100644 --- a/pkg/state/state_exec_tmpl_test.go +++ b/pkg/state/state_exec_tmpl_test.go @@ -31,13 +31,13 @@ func TestHelmState_executeTemplates(t *testing.T) { { name: "Has template expressions in chart, values, secrets, version, labels", input: ReleaseSpec{ - Chart: "test-charts/{{ .Release.Name }}", - Version: "{{ .Release.Name }}-0.1", - Name: "test-app", - Namespace: "test-namespace-{{ .Release.Name }}", - Values: []interface{}{"config/{{ .Environment.Name }}/{{ .Release.Name }}/values.yaml"}, - Secrets: []string{"config/{{ .Environment.Name }}/{{ .Release.Name }}/secrets.yaml"}, - Labels: map[string]string{"id": "{{ .Release.Name }}"}, + Chart: "test-charts/{{ .Release.Name }}", + Version: "{{ .Release.Name }}-0.1", + Name: "test-app", + Namespace: "test-namespace-{{ .Release.Name }}", + ValuesTemplate: []interface{}{"config/{{ .Environment.Name }}/{{ .Release.Name }}/values.yaml"}, + Secrets: []string{"config/{{ .Environment.Name }}/{{ .Release.Name }}/secrets.yaml"}, + Labels: map[string]string{"id": "{{ .Release.Name }}"}, }, want: ReleaseSpec{ Chart: "test-charts/test-app", @@ -94,7 +94,7 @@ func TestHelmState_executeTemplates(t *testing.T) { Chart: "test-charts/chart", Name: "test-app", Namespace: "dev", - SetValues: []SetValue{ + SetValuesTemplate: []SetValue{ SetValue{Name: "val1", Value: "{{ .Release.Name }}-val1"}, SetValue{Name: "val2", File: "{{ .Release.Name }}.yml"}, SetValue{Name: "val3", Values: []string{"{{ .Release.Name }}-val2", "{{ .Release.Name }}-val3"}}, @@ -114,11 +114,11 @@ func TestHelmState_executeTemplates(t *testing.T) { { name: "Has template in values (map)", input: ReleaseSpec{ - Chart: "test-charts/chart", - Verify: nil, - Name: "app", - Namespace: "dev", - Values: []interface{}{map[string]string{"key": "{{ .Release.Name }}-val0"}}, + Chart: "test-charts/chart", + Verify: nil, + Name: "app", + Namespace: "dev", + ValuesTemplate: []interface{}{map[string]string{"key": "{{ .Release.Name }}-val0"}}, }, want: ReleaseSpec{ Chart: "test-charts/chart",