Skip to content

Commit e16c379

Browse files
authored
Merge pull request #147 from wata727/support_environment_variables
Support variables from environment variables
2 parents 2ae5cc9 + b74dcec commit e16c379

File tree

2 files changed

+147
-3
lines changed

2 files changed

+147
-3
lines changed

evaluator/variable.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package evaluator
22

33
import (
4+
"os"
45
"reflect"
6+
"strings"
57

68
"fmt"
79

@@ -31,13 +33,19 @@ func detectVariables(templates map[string]*hclast.File, varfile []*hclast.File)
3133
if err := hcl.DecodeObject(&variables, template.Node.(*hclast.ObjectList).Filter("variable")); err != nil {
3234
return nil, err
3335
}
36+
envVars, err := decodeEnvVars(variables)
37+
if err != nil {
38+
return nil, err
39+
}
3440
tfvars, err := decodeTFVars(varfile)
3541
if err != nil {
3642
return nil, err
3743
}
3844

3945
for _, v := range variables {
40-
if overriddenVariable(v, tfvars); v.Default == nil {
46+
overriddenVariable(v, envVars)
47+
overriddenVariable(v, tfvars)
48+
if v.Default == nil {
4149
continue
4250
}
4351
varName := "var." + v.Name
@@ -61,6 +69,44 @@ func decodeTFVars(varfile []*hclast.File) ([]map[string]interface{}, error) {
6169
return result, nil
6270
}
6371

72+
func decodeEnvVars(variables []*hclVariable) ([]map[string]interface{}, error) {
73+
result := []map[string]interface{}{}
74+
75+
for _, e := range os.Environ() {
76+
idx := strings.Index(e, "=")
77+
envKey := e[:idx]
78+
envVal := e[idx+1:]
79+
80+
if strings.HasPrefix(envKey, "TF_VAR_") {
81+
varName := strings.Replace(envKey, "TF_VAR_", "", 1)
82+
for _, v := range variables {
83+
if v.Name != varName {
84+
continue
85+
}
86+
87+
var varType string
88+
if v.DeclaredType == "" {
89+
varType = deduceType(v.Default)
90+
} else {
91+
varType = v.DeclaredType
92+
}
93+
94+
if varType == HCL_STRING_VARTYPE {
95+
envVal = fmt.Sprintf("\"%s\"", envVal)
96+
}
97+
98+
var r map[string]interface{}
99+
if err := hcl.Decode(&r, fmt.Sprintf("%s = %s", varName, envVal)); err != nil {
100+
return nil, err
101+
}
102+
result = append(result, r)
103+
}
104+
}
105+
}
106+
107+
return result, nil
108+
}
109+
64110
func overriddenVariable(v *hclVariable, tfvars []map[string]interface{}) {
65111
for _, vars := range tfvars {
66112
val := vars[v.Name]
@@ -83,8 +129,13 @@ func overriddenVariable(v *hclVariable, tfvars []map[string]interface{}) {
83129
}
84130
}
85131

86-
func parseVariable(val interface{}, varType string) hilast.Variable {
87-
// varType is overwrite invariably. Because, happen panic when used in incorrect type
132+
func deduceType(val interface{}) string {
133+
if val == nil {
134+
return HCL_STRING_VARTYPE
135+
}
136+
137+
var varType string
138+
88139
switch reflect.TypeOf(val).Kind() {
89140
case reflect.String:
90141
varType = HCL_STRING_VARTYPE
@@ -96,6 +147,13 @@ func parseVariable(val interface{}, varType string) hilast.Variable {
96147
varType = HCL_STRING_VARTYPE
97148
}
98149

150+
return varType
151+
}
152+
153+
func parseVariable(val interface{}, varType string) hilast.Variable {
154+
// varType is overwrite invariably. Because, happen panic when used in incorrect type
155+
varType = deduceType(val)
156+
99157
var hilVar hilast.Variable
100158
switch varType {
101159
case HCL_STRING_VARTYPE:

evaluator/variable_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package evaluator
22

33
import (
4+
"os"
45
"reflect"
56
"testing"
67

@@ -222,6 +223,91 @@ complex = {
222223
}
223224
}
224225

226+
func TestDecodeEnvVars(t *testing.T) {
227+
cases := []struct {
228+
Name string
229+
Vars []*hclVariable
230+
Env map[string]string
231+
Result []map[string]interface{}
232+
}{
233+
{
234+
Name: "Decode string",
235+
Vars: []*hclVariable{
236+
{
237+
Name: "hoge",
238+
},
239+
},
240+
Env: map[string]string{
241+
"TF_VAR_hoge": "fuga",
242+
},
243+
Result: []map[string]interface{}{
244+
{
245+
"hoge": "fuga",
246+
},
247+
},
248+
},
249+
{
250+
Name: "Decode list",
251+
Vars: []*hclVariable{
252+
{
253+
Name: "hoge",
254+
DeclaredType: "list",
255+
},
256+
},
257+
Env: map[string]string{
258+
"TF_VAR_hoge": "[\"bar\", \"baz\"]",
259+
},
260+
Result: []map[string]interface{}{
261+
{
262+
"hoge": []interface{}{"bar", "baz"},
263+
},
264+
},
265+
},
266+
{
267+
Name: "Decode map",
268+
Vars: []*hclVariable{
269+
{
270+
Name: "hoge",
271+
DeclaredType: "map",
272+
},
273+
},
274+
Env: map[string]string{
275+
"TF_VAR_hoge": "{bar = \"baz\", hoge = \"fuga\"}",
276+
},
277+
Result: []map[string]interface{}{
278+
{
279+
"hoge": []map[string]interface{}{
280+
{
281+
"bar": "baz",
282+
"hoge": "fuga",
283+
},
284+
},
285+
},
286+
},
287+
},
288+
}
289+
290+
for _, tc := range cases {
291+
for k, v := range tc.Env {
292+
os.Setenv(k, v)
293+
}
294+
295+
result, err := decodeEnvVars(tc.Vars)
296+
if err != nil {
297+
t.Fatalf("\nAn error occurred.\nError: %s\n\ntestcase: %s", err, tc.Name)
298+
continue
299+
}
300+
301+
if !reflect.DeepEqual(result, tc.Result) {
302+
t.Fatalf("\nBad: %s\nExpected: %s\n\ntestcase: %s", pp.Sprint(result), pp.Sprint(tc.Result), tc.Name)
303+
}
304+
305+
for k, _ := range tc.Env {
306+
os.Unsetenv(k)
307+
}
308+
}
309+
}
310+
225311
func TestOverriddenVariable(t *testing.T) {
226312
cases := []struct {
227313
Name string

0 commit comments

Comments
 (0)