diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go new file mode 100644 index 0000000..b86bf37 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go | |||
@@ -0,0 +1,85 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/hcl" | ||
7 | "github.com/hashicorp/terraform/tfdiags" | ||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | ) | ||
10 | |||
11 | // evaluateResourceForEachExpression interprets a "for_each" argument on a resource. | ||
12 | // | ||
13 | // Returns a cty.Value map, and diagnostics if necessary. It will return nil if | ||
14 | // the expression is nil, and is used to distinguish between an unset for_each and an | ||
15 | // empty map | ||
16 | func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) { | ||
17 | forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx) | ||
18 | if !known { | ||
19 | // Attach a diag as we do with count, with the same downsides | ||
20 | diags = diags.Append(&hcl.Diagnostic{ | ||
21 | Severity: hcl.DiagError, | ||
22 | Summary: "Invalid forEach argument", | ||
23 | Detail: `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`, | ||
24 | }) | ||
25 | } | ||
26 | return forEachMap, diags | ||
27 | } | ||
28 | |||
29 | // evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression | ||
30 | // except that it handles an unknown result by returning an empty map and | ||
31 | // a known = false, rather than by reporting the unknown value as an error | ||
32 | // diagnostic. | ||
33 | func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) { | ||
34 | if expr == nil { | ||
35 | return nil, true, nil | ||
36 | } | ||
37 | |||
38 | forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) | ||
39 | diags = diags.Append(forEachDiags) | ||
40 | if diags.HasErrors() { | ||
41 | return nil, true, diags | ||
42 | } | ||
43 | |||
44 | switch { | ||
45 | case forEachVal.IsNull(): | ||
46 | diags = diags.Append(&hcl.Diagnostic{ | ||
47 | Severity: hcl.DiagError, | ||
48 | Summary: "Invalid for_each argument", | ||
49 | Detail: `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`, | ||
50 | Subject: expr.Range().Ptr(), | ||
51 | }) | ||
52 | return nil, true, diags | ||
53 | case !forEachVal.IsKnown(): | ||
54 | return map[string]cty.Value{}, false, diags | ||
55 | } | ||
56 | |||
57 | if !forEachVal.CanIterateElements() || forEachVal.Type().IsListType() { | ||
58 | diags = diags.Append(&hcl.Diagnostic{ | ||
59 | Severity: hcl.DiagError, | ||
60 | Summary: "Invalid for_each argument", | ||
61 | Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()), | ||
62 | Subject: expr.Range().Ptr(), | ||
63 | }) | ||
64 | return nil, true, diags | ||
65 | } | ||
66 | |||
67 | if forEachVal.Type().IsSetType() { | ||
68 | if forEachVal.Type().ElementType() != cty.String { | ||
69 | diags = diags.Append(&hcl.Diagnostic{ | ||
70 | Severity: hcl.DiagError, | ||
71 | Summary: "Invalid for_each set argument", | ||
72 | Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()), | ||
73 | Subject: expr.Range().Ptr(), | ||
74 | }) | ||
75 | return nil, true, diags | ||
76 | } | ||
77 | } | ||
78 | |||
79 | // If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap | ||
80 | if forEachVal.LengthInt() == 0 { | ||
81 | return map[string]cty.Value{}, true, diags | ||
82 | } | ||
83 | |||
84 | return forEachVal.AsValueMap(), true, nil | ||
85 | } | ||