Skip to content

Commit

Permalink
feat: recursively expad env variables
Browse files Browse the repository at this point in the history
Signed-off-by: Luis Davim <[email protected]>
  • Loading branch information
luisdavim committed May 14, 2022
1 parent d8fb902 commit 377d032
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 12 deletions.
9 changes: 2 additions & 7 deletions internal/app/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"os"
"sort"
"strings"

"github.com/joho/godotenv"
)

const (
Expand Down Expand Up @@ -251,11 +249,8 @@ func (c *cli) readState(s *state) error {
}
}

if len(c.envFiles) != 0 {
err := godotenv.Overload(c.envFiles...)
if err != nil {
return fmt.Errorf("error loading env file: %w", err)
}
if err := prepareEnv(c.envFiles); err != nil {
return err
}

// wipe & create a temporary directory
Expand Down
2 changes: 1 addition & 1 deletion internal/app/state_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (s *state) expand(relativeToFile string) {
if r.Chart != "" {
var download bool
// support env vars in path
r.Chart = os.ExpandEnv(r.Chart)
r.Chart = os.Expand(r.Chart, getEnv)
repoName := strings.Split(r.Chart, "/")[0]
_, isRepo := s.HelmRepos[repoName]
isRepo = isRepo || stringInSlice(repoName, s.PreconfiguredHelmRepos)
Expand Down
39 changes: 35 additions & 4 deletions internal/app/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"unicode/utf8"

"github.com/Masterminds/semver"
"github.com/joho/godotenv"

"github.com/Praqma/helmsman/internal/aws"
"github.com/Praqma/helmsman/internal/azure"
"github.com/Praqma/helmsman/internal/gcs"
Expand Down Expand Up @@ -140,16 +142,45 @@ func readFile(filepath string) string {
return string(data)
}

// getEnv fetches the value for an environment variable
// recusively expanding the variable's value
func getEnv(key string) string {
value := os.Getenv(key)
for envVar.MatchString(value) {
value = os.ExpandEnv(value)
}
return value
}

// prepareEnv loads dotenv files and recusively expands all environment variables
func prepareEnv(envFiles []string) error {
if len(envFiles) != 0 {
err := godotenv.Overload(envFiles...)
if err != nil {
return fmt.Errorf("error loading env file: %w", err)
}
}
for _, e := range os.Environ() {
if !strings.Contains(e, "$") {
continue
}
e = os.Expand(e, getEnv)
pair := strings.SplitN(e, "=", 2)
os.Setenv(pair[0], pair[1])
}
return nil
}

// substituteEnv checks if a string has an env variable (contains '$'), then it returns its value
// if the env variable is empty or unset, an empty string is returned
// if the string does not contain '$', it is returned as is.
func substituteEnv(name string) string {
if strings.Contains(name, "$") {
func substituteEnv(str string) string {
if strings.Contains(str, "$") {
// add $$ escaping for $ strings
os.Setenv("HELMSMAN_DOLLAR", "$")
return os.ExpandEnv(strings.ReplaceAll(name, "$$", "${HELMSMAN_DOLLAR}"))
return os.ExpandEnv(strings.ReplaceAll(str, "$$", "${HELMSMAN_DOLLAR}"))
}
return name
return str
}

// validateEnvVars parses a string line-by-line and detect env variables in
Expand Down
56 changes: 56 additions & 0 deletions internal/app/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,62 @@ import (
"testing"
)

func TestGetEnv(t *testing.T) {
tests := []struct {
name string
expected func(key string) string
key string
}{
{
name: "direct",
key: "BAR",
expected: func(key string) string {
expected := "myValue"
os.Setenv(key, expected)
return expected
},
},
{
name: "string_with_var",
key: "BAR",
expected: func(key string) string {
expected := "contains myValue"
os.Setenv("FOO", "myValue")
os.Setenv(key, "contains ${FOO}")
return expected
},
},
{
name: "nested_one_level",
key: "BAR",
expected: func(key string) string {
expected := "myValue"
os.Setenv("FOO", expected)
os.Setenv(key, "${FOO}")
return expected
},
},
{
name: "nested_two_levels",
key: "BAR",
expected: func(key string) string {
expected := "myValue"
os.Setenv("FOZ", expected)
os.Setenv("FOO", "$FOZ")
os.Setenv(key, "${FOO}")
return expected
},
},
}
for _, tt := range tests {
expected := tt.expected(tt.key)
value := getEnv(tt.key)
if value != expected {
t.Errorf("getEnv() - unexpected value: wanted: %s got: %s", expected, value)
}
}
}

func TestOciRefToFilename(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit 377d032

Please sign in to comment.