diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/eval_variable.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/eval_variable.go | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go b/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go new file mode 100644 index 0000000..e39a33c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go | |||
@@ -0,0 +1,279 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "reflect" | ||
7 | "strconv" | ||
8 | "strings" | ||
9 | |||
10 | "github.com/hashicorp/terraform/config" | ||
11 | "github.com/hashicorp/terraform/config/module" | ||
12 | "github.com/hashicorp/terraform/helper/hilmapstructure" | ||
13 | ) | ||
14 | |||
15 | // EvalTypeCheckVariable is an EvalNode which ensures that the variable | ||
16 | // values which are assigned as inputs to a module (including the root) | ||
17 | // match the types which are either declared for the variables explicitly | ||
18 | // or inferred from the default values. | ||
19 | // | ||
20 | // In order to achieve this three things are required: | ||
21 | // - a map of the proposed variable values | ||
22 | // - the configuration tree of the module in which the variable is | ||
23 | // declared | ||
24 | // - the path to the module (so we know which part of the tree to | ||
25 | // compare the values against). | ||
26 | type EvalTypeCheckVariable struct { | ||
27 | Variables map[string]interface{} | ||
28 | ModulePath []string | ||
29 | ModuleTree *module.Tree | ||
30 | } | ||
31 | |||
32 | func (n *EvalTypeCheckVariable) Eval(ctx EvalContext) (interface{}, error) { | ||
33 | currentTree := n.ModuleTree | ||
34 | for _, pathComponent := range n.ModulePath[1:] { | ||
35 | currentTree = currentTree.Children()[pathComponent] | ||
36 | } | ||
37 | targetConfig := currentTree.Config() | ||
38 | |||
39 | prototypes := make(map[string]config.VariableType) | ||
40 | for _, variable := range targetConfig.Variables { | ||
41 | prototypes[variable.Name] = variable.Type() | ||
42 | } | ||
43 | |||
44 | // Only display a module in an error message if we are not in the root module | ||
45 | modulePathDescription := fmt.Sprintf(" in module %s", strings.Join(n.ModulePath[1:], ".")) | ||
46 | if len(n.ModulePath) == 1 { | ||
47 | modulePathDescription = "" | ||
48 | } | ||
49 | |||
50 | for name, declaredType := range prototypes { | ||
51 | proposedValue, ok := n.Variables[name] | ||
52 | if !ok { | ||
53 | // This means the default value should be used as no overriding value | ||
54 | // has been set. Therefore we should continue as no check is necessary. | ||
55 | continue | ||
56 | } | ||
57 | |||
58 | if proposedValue == config.UnknownVariableValue { | ||
59 | continue | ||
60 | } | ||
61 | |||
62 | switch declaredType { | ||
63 | case config.VariableTypeString: | ||
64 | switch proposedValue.(type) { | ||
65 | case string: | ||
66 | continue | ||
67 | default: | ||
68 | return nil, fmt.Errorf("variable %s%s should be type %s, got %s", | ||
69 | name, modulePathDescription, declaredType.Printable(), hclTypeName(proposedValue)) | ||
70 | } | ||
71 | case config.VariableTypeMap: | ||
72 | switch proposedValue.(type) { | ||
73 | case map[string]interface{}: | ||
74 | continue | ||
75 | default: | ||
76 | return nil, fmt.Errorf("variable %s%s should be type %s, got %s", | ||
77 | name, modulePathDescription, declaredType.Printable(), hclTypeName(proposedValue)) | ||
78 | } | ||
79 | case config.VariableTypeList: | ||
80 | switch proposedValue.(type) { | ||
81 | case []interface{}: | ||
82 | continue | ||
83 | default: | ||
84 | return nil, fmt.Errorf("variable %s%s should be type %s, got %s", | ||
85 | name, modulePathDescription, declaredType.Printable(), hclTypeName(proposedValue)) | ||
86 | } | ||
87 | default: | ||
88 | return nil, fmt.Errorf("variable %s%s should be type %s, got type string", | ||
89 | name, modulePathDescription, declaredType.Printable()) | ||
90 | } | ||
91 | } | ||
92 | |||
93 | return nil, nil | ||
94 | } | ||
95 | |||
96 | // EvalSetVariables is an EvalNode implementation that sets the variables | ||
97 | // explicitly for interpolation later. | ||
98 | type EvalSetVariables struct { | ||
99 | Module *string | ||
100 | Variables map[string]interface{} | ||
101 | } | ||
102 | |||
103 | // TODO: test | ||
104 | func (n *EvalSetVariables) Eval(ctx EvalContext) (interface{}, error) { | ||
105 | ctx.SetVariables(*n.Module, n.Variables) | ||
106 | return nil, nil | ||
107 | } | ||
108 | |||
109 | // EvalVariableBlock is an EvalNode implementation that evaluates the | ||
110 | // given configuration, and uses the final values as a way to set the | ||
111 | // mapping. | ||
112 | type EvalVariableBlock struct { | ||
113 | Config **ResourceConfig | ||
114 | VariableValues map[string]interface{} | ||
115 | } | ||
116 | |||
117 | func (n *EvalVariableBlock) Eval(ctx EvalContext) (interface{}, error) { | ||
118 | // Clear out the existing mapping | ||
119 | for k, _ := range n.VariableValues { | ||
120 | delete(n.VariableValues, k) | ||
121 | } | ||
122 | |||
123 | // Get our configuration | ||
124 | rc := *n.Config | ||
125 | for k, v := range rc.Config { | ||
126 | vKind := reflect.ValueOf(v).Type().Kind() | ||
127 | |||
128 | switch vKind { | ||
129 | case reflect.Slice: | ||
130 | var vSlice []interface{} | ||
131 | if err := hilmapstructure.WeakDecode(v, &vSlice); err == nil { | ||
132 | n.VariableValues[k] = vSlice | ||
133 | continue | ||
134 | } | ||
135 | case reflect.Map: | ||
136 | var vMap map[string]interface{} | ||
137 | if err := hilmapstructure.WeakDecode(v, &vMap); err == nil { | ||
138 | n.VariableValues[k] = vMap | ||
139 | continue | ||
140 | } | ||
141 | default: | ||
142 | var vString string | ||
143 | if err := hilmapstructure.WeakDecode(v, &vString); err == nil { | ||
144 | n.VariableValues[k] = vString | ||
145 | continue | ||
146 | } | ||
147 | } | ||
148 | |||
149 | return nil, fmt.Errorf("Variable value for %s is not a string, list or map type", k) | ||
150 | } | ||
151 | |||
152 | for _, path := range rc.ComputedKeys { | ||
153 | log.Printf("[DEBUG] Setting Unknown Variable Value for computed key: %s", path) | ||
154 | err := n.setUnknownVariableValueForPath(path) | ||
155 | if err != nil { | ||
156 | return nil, err | ||
157 | } | ||
158 | } | ||
159 | |||
160 | return nil, nil | ||
161 | } | ||
162 | |||
163 | func (n *EvalVariableBlock) setUnknownVariableValueForPath(path string) error { | ||
164 | pathComponents := strings.Split(path, ".") | ||
165 | |||
166 | if len(pathComponents) < 1 { | ||
167 | return fmt.Errorf("No path comoponents in %s", path) | ||
168 | } | ||
169 | |||
170 | if len(pathComponents) == 1 { | ||
171 | // Special case the "top level" since we know the type | ||
172 | if _, ok := n.VariableValues[pathComponents[0]]; !ok { | ||
173 | n.VariableValues[pathComponents[0]] = config.UnknownVariableValue | ||
174 | } | ||
175 | return nil | ||
176 | } | ||
177 | |||
178 | // Otherwise find the correct point in the tree and then set to unknown | ||
179 | var current interface{} = n.VariableValues[pathComponents[0]] | ||
180 | for i := 1; i < len(pathComponents); i++ { | ||
181 | switch tCurrent := current.(type) { | ||
182 | case []interface{}: | ||
183 | index, err := strconv.Atoi(pathComponents[i]) | ||
184 | if err != nil { | ||
185 | return fmt.Errorf("Cannot convert %s to slice index in path %s", | ||
186 | pathComponents[i], path) | ||
187 | } | ||
188 | current = tCurrent[index] | ||
189 | case []map[string]interface{}: | ||
190 | index, err := strconv.Atoi(pathComponents[i]) | ||
191 | if err != nil { | ||
192 | return fmt.Errorf("Cannot convert %s to slice index in path %s", | ||
193 | pathComponents[i], path) | ||
194 | } | ||
195 | current = tCurrent[index] | ||
196 | case map[string]interface{}: | ||
197 | if val, hasVal := tCurrent[pathComponents[i]]; hasVal { | ||
198 | current = val | ||
199 | continue | ||
200 | } | ||
201 | |||
202 | tCurrent[pathComponents[i]] = config.UnknownVariableValue | ||
203 | break | ||
204 | } | ||
205 | } | ||
206 | |||
207 | return nil | ||
208 | } | ||
209 | |||
210 | // EvalCoerceMapVariable is an EvalNode implementation that recognizes a | ||
211 | // specific ambiguous HCL parsing situation and resolves it. In HCL parsing, a | ||
212 | // bare map literal is indistinguishable from a list of maps w/ one element. | ||
213 | // | ||
214 | // We take all the same inputs as EvalTypeCheckVariable above, since we need | ||
215 | // both the target type and the proposed value in order to properly coerce. | ||
216 | type EvalCoerceMapVariable struct { | ||
217 | Variables map[string]interface{} | ||
218 | ModulePath []string | ||
219 | ModuleTree *module.Tree | ||
220 | } | ||
221 | |||
222 | // Eval implements the EvalNode interface. See EvalCoerceMapVariable for | ||
223 | // details. | ||
224 | func (n *EvalCoerceMapVariable) Eval(ctx EvalContext) (interface{}, error) { | ||
225 | currentTree := n.ModuleTree | ||
226 | for _, pathComponent := range n.ModulePath[1:] { | ||
227 | currentTree = currentTree.Children()[pathComponent] | ||
228 | } | ||
229 | targetConfig := currentTree.Config() | ||
230 | |||
231 | prototypes := make(map[string]config.VariableType) | ||
232 | for _, variable := range targetConfig.Variables { | ||
233 | prototypes[variable.Name] = variable.Type() | ||
234 | } | ||
235 | |||
236 | for name, declaredType := range prototypes { | ||
237 | if declaredType != config.VariableTypeMap { | ||
238 | continue | ||
239 | } | ||
240 | |||
241 | proposedValue, ok := n.Variables[name] | ||
242 | if !ok { | ||
243 | continue | ||
244 | } | ||
245 | |||
246 | if list, ok := proposedValue.([]interface{}); ok && len(list) == 1 { | ||
247 | if m, ok := list[0].(map[string]interface{}); ok { | ||
248 | log.Printf("[DEBUG] EvalCoerceMapVariable: "+ | ||
249 | "Coercing single element list into map: %#v", m) | ||
250 | n.Variables[name] = m | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return nil, nil | ||
256 | } | ||
257 | |||
258 | // hclTypeName returns the name of the type that would represent this value in | ||
259 | // a config file, or falls back to the Go type name if there's no corresponding | ||
260 | // HCL type. This is used for formatted output, not for comparing types. | ||
261 | func hclTypeName(i interface{}) string { | ||
262 | switch k := reflect.Indirect(reflect.ValueOf(i)).Kind(); k { | ||
263 | case reflect.Bool: | ||
264 | return "boolean" | ||
265 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | ||
266 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, | ||
267 | reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: | ||
268 | return "number" | ||
269 | case reflect.Array, reflect.Slice: | ||
270 | return "list" | ||
271 | case reflect.Map: | ||
272 | return "map" | ||
273 | case reflect.String: | ||
274 | return "string" | ||
275 | default: | ||
276 | // fall back to the Go type if there's no match | ||
277 | return k.String() | ||
278 | } | ||
279 | } | ||