diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/configs/named_values.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/configs/named_values.go | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/configs/named_values.go b/vendor/github.com/hashicorp/terraform/configs/named_values.go new file mode 100644 index 0000000..6f6b469 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/named_values.go | |||
@@ -0,0 +1,364 @@ | |||
1 | package configs | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/ext/typeexpr" | ||
7 | "github.com/hashicorp/hcl2/gohcl" | ||
8 | "github.com/hashicorp/hcl2/hcl" | ||
9 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | ||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | "github.com/zclconf/go-cty/cty/convert" | ||
12 | |||
13 | "github.com/hashicorp/terraform/addrs" | ||
14 | ) | ||
15 | |||
16 | // A consistent detail message for all "not a valid identifier" diagnostics. | ||
17 | const badIdentifierDetail = "A name must start with a letter and may contain only letters, digits, underscores, and dashes." | ||
18 | |||
19 | // Variable represents a "variable" block in a module or file. | ||
20 | type Variable struct { | ||
21 | Name string | ||
22 | Description string | ||
23 | Default cty.Value | ||
24 | Type cty.Type | ||
25 | ParsingMode VariableParsingMode | ||
26 | |||
27 | DescriptionSet bool | ||
28 | |||
29 | DeclRange hcl.Range | ||
30 | } | ||
31 | |||
32 | func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagnostics) { | ||
33 | v := &Variable{ | ||
34 | Name: block.Labels[0], | ||
35 | DeclRange: block.DefRange, | ||
36 | } | ||
37 | |||
38 | // Unless we're building an override, we'll set some defaults | ||
39 | // which we might override with attributes below. We leave these | ||
40 | // as zero-value in the override case so we can recognize whether | ||
41 | // or not they are set when we merge. | ||
42 | if !override { | ||
43 | v.Type = cty.DynamicPseudoType | ||
44 | v.ParsingMode = VariableParseLiteral | ||
45 | } | ||
46 | |||
47 | content, diags := block.Body.Content(variableBlockSchema) | ||
48 | |||
49 | if !hclsyntax.ValidIdentifier(v.Name) { | ||
50 | diags = append(diags, &hcl.Diagnostic{ | ||
51 | Severity: hcl.DiagError, | ||
52 | Summary: "Invalid variable name", | ||
53 | Detail: badIdentifierDetail, | ||
54 | Subject: &block.LabelRanges[0], | ||
55 | }) | ||
56 | } | ||
57 | |||
58 | // Don't allow declaration of variables that would conflict with the | ||
59 | // reserved attribute and block type names in a "module" block, since | ||
60 | // these won't be usable for child modules. | ||
61 | for _, attr := range moduleBlockSchema.Attributes { | ||
62 | if attr.Name == v.Name { | ||
63 | diags = append(diags, &hcl.Diagnostic{ | ||
64 | Severity: hcl.DiagError, | ||
65 | Summary: "Invalid variable name", | ||
66 | Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", attr.Name), | ||
67 | Subject: &block.LabelRanges[0], | ||
68 | }) | ||
69 | } | ||
70 | } | ||
71 | for _, blockS := range moduleBlockSchema.Blocks { | ||
72 | if blockS.Type == v.Name { | ||
73 | diags = append(diags, &hcl.Diagnostic{ | ||
74 | Severity: hcl.DiagError, | ||
75 | Summary: "Invalid variable name", | ||
76 | Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", blockS.Type), | ||
77 | Subject: &block.LabelRanges[0], | ||
78 | }) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | if attr, exists := content.Attributes["description"]; exists { | ||
83 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description) | ||
84 | diags = append(diags, valDiags...) | ||
85 | v.DescriptionSet = true | ||
86 | } | ||
87 | |||
88 | if attr, exists := content.Attributes["type"]; exists { | ||
89 | ty, parseMode, tyDiags := decodeVariableType(attr.Expr) | ||
90 | diags = append(diags, tyDiags...) | ||
91 | v.Type = ty | ||
92 | v.ParsingMode = parseMode | ||
93 | } | ||
94 | |||
95 | if attr, exists := content.Attributes["default"]; exists { | ||
96 | val, valDiags := attr.Expr.Value(nil) | ||
97 | diags = append(diags, valDiags...) | ||
98 | |||
99 | // Convert the default to the expected type so we can catch invalid | ||
100 | // defaults early and allow later code to assume validity. | ||
101 | // Note that this depends on us having already processed any "type" | ||
102 | // attribute above. | ||
103 | // However, we can't do this if we're in an override file where | ||
104 | // the type might not be set; we'll catch that during merge. | ||
105 | if v.Type != cty.NilType { | ||
106 | var err error | ||
107 | val, err = convert.Convert(val, v.Type) | ||
108 | if err != nil { | ||
109 | diags = append(diags, &hcl.Diagnostic{ | ||
110 | Severity: hcl.DiagError, | ||
111 | Summary: "Invalid default value for variable", | ||
112 | Detail: fmt.Sprintf("This default value is not compatible with the variable's type constraint: %s.", err), | ||
113 | Subject: attr.Expr.Range().Ptr(), | ||
114 | }) | ||
115 | val = cty.DynamicVal | ||
116 | } | ||
117 | } | ||
118 | |||
119 | v.Default = val | ||
120 | } | ||
121 | |||
122 | return v, diags | ||
123 | } | ||
124 | |||
125 | func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl.Diagnostics) { | ||
126 | if exprIsNativeQuotedString(expr) { | ||
127 | // Here we're accepting the pre-0.12 form of variable type argument where | ||
128 | // the string values "string", "list" and "map" are accepted has a hint | ||
129 | // about the type used primarily for deciding how to parse values | ||
130 | // given on the command line and in environment variables. | ||
131 | // Only the native syntax ends up in this codepath; we handle the | ||
132 | // JSON syntax (which is, of course, quoted even in the new format) | ||
133 | // in the normal codepath below. | ||
134 | val, diags := expr.Value(nil) | ||
135 | if diags.HasErrors() { | ||
136 | return cty.DynamicPseudoType, VariableParseHCL, diags | ||
137 | } | ||
138 | str := val.AsString() | ||
139 | switch str { | ||
140 | case "string": | ||
141 | return cty.String, VariableParseLiteral, diags | ||
142 | case "list": | ||
143 | return cty.List(cty.DynamicPseudoType), VariableParseHCL, diags | ||
144 | case "map": | ||
145 | return cty.Map(cty.DynamicPseudoType), VariableParseHCL, diags | ||
146 | default: | ||
147 | return cty.DynamicPseudoType, VariableParseHCL, hcl.Diagnostics{{ | ||
148 | Severity: hcl.DiagError, | ||
149 | Summary: "Invalid legacy variable type hint", | ||
150 | Detail: `The legacy variable type hint form, using a quoted string, allows only the values "string", "list", and "map". To provide a full type expression, remove the surrounding quotes and give the type expression directly.`, | ||
151 | Subject: expr.Range().Ptr(), | ||
152 | }} | ||
153 | } | ||
154 | } | ||
155 | |||
156 | // First we'll deal with some shorthand forms that the HCL-level type | ||
157 | // expression parser doesn't include. These both emulate pre-0.12 behavior | ||
158 | // of allowing a list or map of any element type as long as all of the | ||
159 | // elements are consistent. This is the same as list(any) or map(any). | ||
160 | switch hcl.ExprAsKeyword(expr) { | ||
161 | case "list": | ||
162 | return cty.List(cty.DynamicPseudoType), VariableParseHCL, nil | ||
163 | case "map": | ||
164 | return cty.Map(cty.DynamicPseudoType), VariableParseHCL, nil | ||
165 | } | ||
166 | |||
167 | ty, diags := typeexpr.TypeConstraint(expr) | ||
168 | if diags.HasErrors() { | ||
169 | return cty.DynamicPseudoType, VariableParseHCL, diags | ||
170 | } | ||
171 | |||
172 | switch { | ||
173 | case ty.IsPrimitiveType(): | ||
174 | // Primitive types use literal parsing. | ||
175 | return ty, VariableParseLiteral, diags | ||
176 | default: | ||
177 | // Everything else uses HCL parsing | ||
178 | return ty, VariableParseHCL, diags | ||
179 | } | ||
180 | } | ||
181 | |||
182 | // VariableParsingMode defines how values of a particular variable given by | ||
183 | // text-only mechanisms (command line arguments and environment variables) | ||
184 | // should be parsed to produce the final value. | ||
185 | type VariableParsingMode rune | ||
186 | |||
187 | // VariableParseLiteral is a variable parsing mode that just takes the given | ||
188 | // string directly as a cty.String value. | ||
189 | const VariableParseLiteral VariableParsingMode = 'L' | ||
190 | |||
191 | // VariableParseHCL is a variable parsing mode that attempts to parse the given | ||
192 | // string as an HCL expression and returns the result. | ||
193 | const VariableParseHCL VariableParsingMode = 'H' | ||
194 | |||
195 | // Parse uses the receiving parsing mode to process the given variable value | ||
196 | // string, returning the result along with any diagnostics. | ||
197 | // | ||
198 | // A VariableParsingMode does not know the expected type of the corresponding | ||
199 | // variable, so it's the caller's responsibility to attempt to convert the | ||
200 | // result to the appropriate type and return to the user any diagnostics that | ||
201 | // conversion may produce. | ||
202 | // | ||
203 | // The given name is used to create a synthetic filename in case any diagnostics | ||
204 | // must be generated about the given string value. This should be the name | ||
205 | // of the root module variable whose value will be populated from the given | ||
206 | // string. | ||
207 | // | ||
208 | // If the returned diagnostics has errors, the returned value may not be | ||
209 | // valid. | ||
210 | func (m VariableParsingMode) Parse(name, value string) (cty.Value, hcl.Diagnostics) { | ||
211 | switch m { | ||
212 | case VariableParseLiteral: | ||
213 | return cty.StringVal(value), nil | ||
214 | case VariableParseHCL: | ||
215 | fakeFilename := fmt.Sprintf("<value for var.%s>", name) | ||
216 | expr, diags := hclsyntax.ParseExpression([]byte(value), fakeFilename, hcl.Pos{Line: 1, Column: 1}) | ||
217 | if diags.HasErrors() { | ||
218 | return cty.DynamicVal, diags | ||
219 | } | ||
220 | val, valDiags := expr.Value(nil) | ||
221 | diags = append(diags, valDiags...) | ||
222 | return val, diags | ||
223 | default: | ||
224 | // Should never happen | ||
225 | panic(fmt.Errorf("Parse called on invalid VariableParsingMode %#v", m)) | ||
226 | } | ||
227 | } | ||
228 | |||
229 | // Output represents an "output" block in a module or file. | ||
230 | type Output struct { | ||
231 | Name string | ||
232 | Description string | ||
233 | Expr hcl.Expression | ||
234 | DependsOn []hcl.Traversal | ||
235 | Sensitive bool | ||
236 | |||
237 | DescriptionSet bool | ||
238 | SensitiveSet bool | ||
239 | |||
240 | DeclRange hcl.Range | ||
241 | } | ||
242 | |||
243 | func decodeOutputBlock(block *hcl.Block, override bool) (*Output, hcl.Diagnostics) { | ||
244 | o := &Output{ | ||
245 | Name: block.Labels[0], | ||
246 | DeclRange: block.DefRange, | ||
247 | } | ||
248 | |||
249 | schema := outputBlockSchema | ||
250 | if override { | ||
251 | schema = schemaForOverrides(schema) | ||
252 | } | ||
253 | |||
254 | content, diags := block.Body.Content(schema) | ||
255 | |||
256 | if !hclsyntax.ValidIdentifier(o.Name) { | ||
257 | diags = append(diags, &hcl.Diagnostic{ | ||
258 | Severity: hcl.DiagError, | ||
259 | Summary: "Invalid output name", | ||
260 | Detail: badIdentifierDetail, | ||
261 | Subject: &block.LabelRanges[0], | ||
262 | }) | ||
263 | } | ||
264 | |||
265 | if attr, exists := content.Attributes["description"]; exists { | ||
266 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Description) | ||
267 | diags = append(diags, valDiags...) | ||
268 | o.DescriptionSet = true | ||
269 | } | ||
270 | |||
271 | if attr, exists := content.Attributes["value"]; exists { | ||
272 | o.Expr = attr.Expr | ||
273 | } | ||
274 | |||
275 | if attr, exists := content.Attributes["sensitive"]; exists { | ||
276 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Sensitive) | ||
277 | diags = append(diags, valDiags...) | ||
278 | o.SensitiveSet = true | ||
279 | } | ||
280 | |||
281 | if attr, exists := content.Attributes["depends_on"]; exists { | ||
282 | deps, depsDiags := decodeDependsOn(attr) | ||
283 | diags = append(diags, depsDiags...) | ||
284 | o.DependsOn = append(o.DependsOn, deps...) | ||
285 | } | ||
286 | |||
287 | return o, diags | ||
288 | } | ||
289 | |||
290 | // Local represents a single entry from a "locals" block in a module or file. | ||
291 | // The "locals" block itself is not represented, because it serves only to | ||
292 | // provide context for us to interpret its contents. | ||
293 | type Local struct { | ||
294 | Name string | ||
295 | Expr hcl.Expression | ||
296 | |||
297 | DeclRange hcl.Range | ||
298 | } | ||
299 | |||
300 | func decodeLocalsBlock(block *hcl.Block) ([]*Local, hcl.Diagnostics) { | ||
301 | attrs, diags := block.Body.JustAttributes() | ||
302 | if len(attrs) == 0 { | ||
303 | return nil, diags | ||
304 | } | ||
305 | |||
306 | locals := make([]*Local, 0, len(attrs)) | ||
307 | for name, attr := range attrs { | ||
308 | if !hclsyntax.ValidIdentifier(name) { | ||
309 | diags = append(diags, &hcl.Diagnostic{ | ||
310 | Severity: hcl.DiagError, | ||
311 | Summary: "Invalid local value name", | ||
312 | Detail: badIdentifierDetail, | ||
313 | Subject: &attr.NameRange, | ||
314 | }) | ||
315 | } | ||
316 | |||
317 | locals = append(locals, &Local{ | ||
318 | Name: name, | ||
319 | Expr: attr.Expr, | ||
320 | DeclRange: attr.Range, | ||
321 | }) | ||
322 | } | ||
323 | return locals, diags | ||
324 | } | ||
325 | |||
326 | // Addr returns the address of the local value declared by the receiver, | ||
327 | // relative to its containing module. | ||
328 | func (l *Local) Addr() addrs.LocalValue { | ||
329 | return addrs.LocalValue{ | ||
330 | Name: l.Name, | ||
331 | } | ||
332 | } | ||
333 | |||
334 | var variableBlockSchema = &hcl.BodySchema{ | ||
335 | Attributes: []hcl.AttributeSchema{ | ||
336 | { | ||
337 | Name: "description", | ||
338 | }, | ||
339 | { | ||
340 | Name: "default", | ||
341 | }, | ||
342 | { | ||
343 | Name: "type", | ||
344 | }, | ||
345 | }, | ||
346 | } | ||
347 | |||
348 | var outputBlockSchema = &hcl.BodySchema{ | ||
349 | Attributes: []hcl.AttributeSchema{ | ||
350 | { | ||
351 | Name: "description", | ||
352 | }, | ||
353 | { | ||
354 | Name: "value", | ||
355 | Required: true, | ||
356 | }, | ||
357 | { | ||
358 | Name: "depends_on", | ||
359 | }, | ||
360 | { | ||
361 | Name: "sensitive", | ||
362 | }, | ||
363 | }, | ||
364 | } | ||