From ca6dd7a60f3d3b5e1c70cd8023ea7f493214dc2d Mon Sep 17 00:00:00 2001
From: pjotre86 <peter.aichinger@gmx.net>
Date: Thu, 4 Nov 2021 02:04:01 +0100
Subject: [PATCH] Make .Environment values available in .gotmpl files. (#2000)

Currently it's not possible to use `.Environment` values in `*.gomtpl` files. The documentation states the opposite:
https://github.com/roboll/helmfile#environment (2nd paragraph).
The problem is already described in #1090.
This PR fixes this bug.
Fixes #1090

Co-authored-by: Peter Aichinger <petera@topdesk.com>
---
 pkg/app/desired_state_file_loader.go |  2 +-
 pkg/state/create.go                  |  8 ++++----
 pkg/state/create_test.go             |  8 +++++++-
 pkg/state/envvals_loader.go          | 13 ++++++++++---
 pkg/state/envvals_loader_test.go     | 15 ++++++++-------
 5 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/pkg/app/desired_state_file_loader.go b/pkg/app/desired_state_file_loader.go
index 9e5bc4854..4cbffd004 100644
--- a/pkg/app/desired_state_file_loader.go
+++ b/pkg/app/desired_state_file_loader.go
@@ -52,7 +52,7 @@ func (ld *desiredStateLoader) Load(f string, opts LoadOpts) (*state.HelmState, e
 		storage := state.NewStorage(opts.CalleePath, ld.logger, ld.glob)
 		envld := state.NewEnvironmentValuesLoader(storage, ld.readFile, ld.logger, ld.remote)
 		handler := state.MissingFileHandlerError
-		vals, err := envld.LoadEnvironmentValues(&handler, args)
+		vals, err := envld.LoadEnvironmentValues(&handler, args, &environment.EmptyEnvironment)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/state/create.go b/pkg/state/create.go
index a1830fae9..9ad20be1b 100644
--- a/pkg/state/create.go
+++ b/pkg/state/create.go
@@ -153,7 +153,7 @@ func (c *StateCreator) LoadEnvValues(target *HelmState, env string, ctxEnv *envi
 		return nil, &StateLoadError{fmt.Sprintf("failed to read %s", state.FilePath), err}
 	}
 
-	newDefaults, err := state.loadValuesEntries(nil, state.DefaultValues, c.remote)
+	newDefaults, err := state.loadValuesEntries(nil, state.DefaultValues, c.remote, ctxEnv)
 	if err != nil {
 		return nil, err
 	}
@@ -227,7 +227,7 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn
 	envSpec, ok := st.Environments[name]
 	if ok {
 		var err error
-		envVals, err = st.loadValuesEntries(envSpec.MissingFileHandler, envSpec.Values, c.remote)
+		envVals, err = st.loadValuesEntries(envSpec.MissingFileHandler, envSpec.Values, c.remote, ctxEnv)
 		if err != nil {
 			return nil, err
 		}
@@ -363,13 +363,13 @@ func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles
 	return nil
 }
 
-func (st *HelmState) loadValuesEntries(missingFileHandler *string, entries []interface{}, remote *remote.Remote) (map[string]interface{}, error) {
+func (st *HelmState) loadValuesEntries(missingFileHandler *string, entries []interface{}, remote *remote.Remote, ctxEnv *environment.Environment) (map[string]interface{}, error) {
 	envVals := map[string]interface{}{}
 
 	valuesEntries := append([]interface{}{}, entries...)
 	ld := NewEnvironmentValuesLoader(st.storage(), st.readFile, st.logger, remote)
 	var err error
-	envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries)
+	envVals, err = ld.LoadEnvironmentValues(missingFileHandler, valuesEntries, ctxEnv)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/state/create_test.go b/pkg/state/create_test.go
index 656fc119e..5322d5f6f 100644
--- a/pkg/state/create_test.go
+++ b/pkg/state/create_test.go
@@ -7,6 +7,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/roboll/helmfile/pkg/environment"
 	"github.com/roboll/helmfile/pkg/remote"
 
 	"github.com/roboll/helmfile/pkg/testhelper"
@@ -117,6 +118,7 @@ baz: "{{ readFile \"baz.txt\" }}"`)
 	barYamlFile := "/example/path/to/bar.yaml.gotmpl"
 	barYamlContent := []byte(`foo: FOO
 bar: {{ readFile "bar.txt" }}
+env: {{ .Environment.Name }}
 `)
 
 	barTextFile := "/example/path/to/bar.txt"
@@ -127,6 +129,7 @@ bar: {{ readFile "bar.txt" }}
 		"bar": "BAR",
 		// As the file doesn't have an file extension ".gotmpl", this template expression should not be evaluated
 		"baz": "{{ readFile \"baz.txt\" }}",
+		"env": "production",
 	}
 
 	valuesFile := "/example/path/to/values.yaml.gotmpl"
@@ -149,8 +152,11 @@ releaseNamespace: mynamespace
 	testFs.Cwd = "/example/path/to"
 
 	r := remote.NewRemote(logger, testFs.Cwd, testFs.ReadFile, testFs.DirectoryExistsAt, testFs.FileExistsAt)
+	env := environment.Environment{
+		Name: "production",
+	}
 	state, err := NewCreator(logger, testFs.ReadFile, testFs.FileExists, testFs.Abs, testFs.Glob, testFs.DirectoryExistsAt, nil, nil, "", r).
-		ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, nil)
+		ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "production", true, &env)
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
diff --git a/pkg/state/envvals_loader.go b/pkg/state/envvals_loader.go
index 53d605a9b..cd73ed3aa 100644
--- a/pkg/state/envvals_loader.go
+++ b/pkg/state/envvals_loader.go
@@ -2,6 +2,8 @@ package state
 
 import (
 	"fmt"
+	"path/filepath"
+
 	"github.com/imdario/mergo"
 	"github.com/roboll/helmfile/pkg/environment"
 	"github.com/roboll/helmfile/pkg/maputil"
@@ -9,7 +11,6 @@ import (
 	"github.com/roboll/helmfile/pkg/tmpl"
 	"go.uber.org/zap"
 	"gopkg.in/yaml.v2"
-	"path/filepath"
 )
 
 type EnvironmentValuesLoader struct {
@@ -31,7 +32,7 @@ func NewEnvironmentValuesLoader(storage *Storage, readFile func(string) ([]byte,
 	}
 }
 
-func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *string, valuesEntries []interface{}) (map[string]interface{}, error) {
+func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *string, valuesEntries []interface{}, ctxEnv *environment.Environment) (map[string]interface{}, error) {
 	result := map[string]interface{}{}
 
 	for _, entry := range valuesEntries {
@@ -54,7 +55,13 @@ func (ld *EnvironmentValuesLoader) LoadEnvironmentValues(missingFileHandler *str
 			}
 
 			for _, f := range files {
-				tmplData := EnvironmentTemplateData{environment.EmptyEnvironment, "", map[string]interface{}{}}
+				var env environment.Environment
+				if ctxEnv == nil {
+					env = environment.EmptyEnvironment
+				} else {
+					env = *ctxEnv
+				}
+				tmplData := EnvironmentTemplateData{env, "", map[string]interface{}{}}
 				r := tmpl.NewFileRenderer(ld.readFile, filepath.Dir(f), tmplData)
 				bytes, err := r.RenderToBytes(f)
 				if err != nil {
diff --git a/pkg/state/envvals_loader_test.go b/pkg/state/envvals_loader_test.go
index bb6feb201..264591e02 100644
--- a/pkg/state/envvals_loader_test.go
+++ b/pkg/state/envvals_loader_test.go
@@ -1,12 +1,13 @@
 package state
 
 import (
-	"github.com/google/go-cmp/cmp"
-	"github.com/roboll/helmfile/pkg/remote"
-	"go.uber.org/zap"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/roboll/helmfile/pkg/remote"
+	"go.uber.org/zap"
 )
 
 func newLoader() *EnvironmentValuesLoader {
@@ -34,7 +35,7 @@ func newLoader() *EnvironmentValuesLoader {
 func TestEnvValsLoad_SingleValuesFile(t *testing.T) {
 	l := newLoader()
 
-	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.5.yaml"})
+	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.5.yaml"}, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -52,7 +53,7 @@ func TestEnvValsLoad_SingleValuesFile(t *testing.T) {
 func TestEnvValsLoad_OverwriteNilValue_Issue1150(t *testing.T) {
 	l := newLoader()
 
-	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.1.yaml", "testdata/values.2.yaml"})
+	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.1.yaml", "testdata/values.2.yaml"}, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -74,7 +75,7 @@ func TestEnvValsLoad_OverwriteNilValue_Issue1150(t *testing.T) {
 func TestEnvValsLoad_OverwriteWithNilValue_Issue1154(t *testing.T) {
 	l := newLoader()
 
-	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.3.yaml", "testdata/values.4.yaml"})
+	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/values.3.yaml", "testdata/values.4.yaml"}, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -97,7 +98,7 @@ func TestEnvValsLoad_OverwriteWithNilValue_Issue1154(t *testing.T) {
 func TestEnvValsLoad_OverwriteEmptyValue_Issue1168(t *testing.T) {
 	l := newLoader()
 
-	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/issues/1168/addons.yaml", "testdata/issues/1168/addons2.yaml"})
+	actual, err := l.LoadEnvironmentValues(nil, []interface{}{"testdata/issues/1168/addons.yaml", "testdata/issues/1168/addons2.yaml"}, nil)
 	if err != nil {
 		t.Fatal(err)
 	}