Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace all ${VAR} references with env var values in watches yaml #45

Merged
merged 4 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions internal/ansible/watches/testdata/env-vars.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
- version: "${WATCH_VERSION}"
group: "app.example.com"
kind: "AnsibleSelectorTest"
playbook: ${WATCH_PLAYBOOK}
selector:
matchLabels:
${WATCH_MATCH_LABEL_VAR_NAME}: "${WATCH_MATCH_LABEL_VAR_VALUE}"
undefined: "${WATCH_UNDEFINED_ENV_VAR}"
matchExpressions:
- {key: ${WATCH_MATCH_EXPRESSIONS_KEY}, operator: In, values: [a, b]}
21 changes: 20 additions & 1 deletion internal/ansible/watches/watches.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,15 @@ func New(gvk schema.GroupVersionKind, role, playbook string, vars map[string]int
func Load(path string, maxReconciler, ansibleVerbosity int) ([]Watch, error) {
maxConcurrentReconcilesDefault = maxReconciler
ansibleVerbosityDefault = ansibleVerbosity
b, err := os.ReadFile(path)
fileb, err := os.ReadFile(path)
if err != nil {
log.Error(err, "Failed to get config file")
return nil, err
}

// Replace any environment variable references with their values
b := replaceEnvVariables(fileb)

// First unmarshal into a slice of aliases.
alias := []alias{}
err = yaml.Unmarshal(b, &alias)
Expand Down Expand Up @@ -358,6 +361,22 @@ func Load(path string, maxReconciler, ansibleVerbosity int) ([]Watch, error) {
return watches, nil
}

// replaceEnvVariables will replace all ${VAR} references found in the byte array
// with the actual values of the env variables. If an env variable is not defined
// the ${VAR} reference remains as-is in the returned byte array.
func replaceEnvVariables(input []byte) []byte {
outputString := os.Expand(string(input), func(varName string) string {
varValue, exists := os.LookupEnv(varName)
if !exists {
// If the environment variable doesn't exist, keep the original reference
return "${" + varName + "}"
everettraven marked this conversation as resolved.
Show resolved Hide resolved
}
return varValue
})

return []byte(outputString)
}

// verify that a given GroupVersionKind has a Version and Kind
// A GVK without a group is valid. Certain scenarios may cause a GVK
// without a group to fail in other ways later in the initialization
Expand Down
41 changes: 41 additions & 0 deletions internal/ansible/watches/watches_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -892,3 +892,44 @@ func TestGetPossibleRolePaths(t *testing.T) {
})
}
}

func TestReplaceEnvVars(t *testing.T) {
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("Unable to get working directory: %v", err)
}
watchesFile := filepath.Join(cwd, "testdata", "env-vars.yaml")
playbookFile := filepath.Join(cwd, "testdata", "playbook.yml")

t.Setenv("WATCH_PLAYBOOK", playbookFile)
t.Setenv("WATCH_VERSION", "v123")
t.Setenv("WATCH_MATCH_LABEL_VAR_NAME", "label123")
t.Setenv("WATCH_MATCH_LABEL_VAR_VALUE", "value123")
t.Setenv("WATCH_MATCH_EXPRESSIONS_KEY", "key123")

watchSlice, err := Load(watchesFile, 1, 1)
if err != nil {
// this would fail if the WATCH_PLAYBOOK env var wasn't replaced
t.Fatalf("Failed to process watches yaml with env vars: %v", err)
}
if watchSlice[0].GroupVersionKind.Group != "app.example.com" {
t.Fatalf("Failed to parse hardcoded group - the watches yaml did not load properly: %+v", watchSlice[0])
}
if watchSlice[0].GroupVersionKind.Version != "v123" {
t.Fatalf("Failed to replace version with env var: %+v", watchSlice[0])
}
if labelValue, ok := watchSlice[0].Selector.MatchLabels["label123"]; !ok {
t.Fatalf("Failed to replace first match label name with env var: %+v", watchSlice[0])
} else if labelValue != "value123" {
t.Fatalf("Failed to replace first match label value with env var: %+v", watchSlice[0])
}
if labelValue, ok := watchSlice[0].Selector.MatchLabels["undefined"]; !ok {
t.Fatalf("Failed to find the second match label name: %+v", watchSlice[0])
} else if labelValue != "${WATCH_UNDEFINED_ENV_VAR}" {
t.Fatalf("Failed to keep env var reference as-is: %+v", watchSlice[0])
}
keyValue := watchSlice[0].Selector.MatchExpressions[0].Key
if keyValue != "key123" {
t.Fatalf("Failed to replace match expression key with env var: %+v", watchSlice[0])
}
}