Repository: roboll/helmfile
Pull request #833 (2019-08-31 09:48:21)

Make advanced templating backward-compatible
This is a follow-up for #823
Ref https://github.com/roboll/helmfile/pull/823#discussion_r319712283


Patch:
diff --git a/docs/writing-helmfile.md b/docs/writing-helmfile.md
index 285c412..e621d28 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,7 +110,7 @@ Release Templating supports the following parts of release definition:
 - `values` and `secrets` file paths:
   ```yaml
   # ...
-    values:
+    valuesTemplate:
     - config/{{`{{ .Release.Name }}`}}/values.yaml
     secrets:
     - config/{{`{{ .Release.Name }}`}}/secrets.yaml
@@ -119,7 +119,7 @@ Release Templating supports the following parts of release definition:
 - inline `values` map:
   ```yaml
   # ...
-    values:
+    valuesTemplate:
     - image:
         tag: `{{ .Release.Labels.tag }}`
   # ...
diff --git a/pkg/state/release.go b/pkg/state/release.go
index 6a954ba..3ea0c24 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 6a74962..c6fd53f 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"`
 


Test patch:
diff --git a/pkg/state/state_exec_tmpl_test.go b/pkg/state/state_exec_tmpl_test.go
index 504caad..05aa00f 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",