]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/terraform/eval_count.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / eval_count.go
1 package terraform
2
3 import (
4 "fmt"
5 "log"
6
7 "github.com/hashicorp/hcl2/hcl"
8 "github.com/hashicorp/terraform/addrs"
9 "github.com/hashicorp/terraform/tfdiags"
10 "github.com/zclconf/go-cty/cty"
11 "github.com/zclconf/go-cty/cty/gocty"
12 )
13
14 // evaluateResourceCountExpression is our standard mechanism for interpreting an
15 // expression given for a "count" argument on a resource. This should be called
16 // from the DynamicExpand of a node representing a resource in order to
17 // determine the final count value.
18 //
19 // If the result is zero or positive and no error diagnostics are returned, then
20 // the result is the literal count value to use.
21 //
22 // If the result is -1, this indicates that the given expression is nil and so
23 // the "count" behavior should not be enabled for this resource at all.
24 //
25 // If error diagnostics are returned then the result is always the meaningless
26 // placeholder value -1.
27 func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) {
28 count, known, diags := evaluateResourceCountExpressionKnown(expr, ctx)
29 if !known {
30 // Currently this is a rather bad outcome from a UX standpoint, since we have
31 // no real mechanism to deal with this situation and all we can do is produce
32 // an error message.
33 // FIXME: In future, implement a built-in mechanism for deferring changes that
34 // can't yet be predicted, and use it to guide the user through several
35 // plan/apply steps until the desired configuration is eventually reached.
36 diags = diags.Append(&hcl.Diagnostic{
37 Severity: hcl.DiagError,
38 Summary: "Invalid count argument",
39 Detail: `The "count" 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 count depends on.`,
40 Subject: expr.Range().Ptr(),
41 })
42 }
43 return count, diags
44 }
45
46 // evaluateResourceCountExpressionKnown is like evaluateResourceCountExpression
47 // except that it handles an unknown result by returning count = 0 and
48 // a known = false, rather than by reporting the unknown value as an error
49 // diagnostic.
50 func evaluateResourceCountExpressionKnown(expr hcl.Expression, ctx EvalContext) (count int, known bool, diags tfdiags.Diagnostics) {
51 if expr == nil {
52 return -1, true, nil
53 }
54
55 countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
56 diags = diags.Append(countDiags)
57 if diags.HasErrors() {
58 return -1, true, diags
59 }
60
61 switch {
62 case countVal.IsNull():
63 diags = diags.Append(&hcl.Diagnostic{
64 Severity: hcl.DiagError,
65 Summary: "Invalid count argument",
66 Detail: `The given "count" argument value is null. An integer is required.`,
67 Subject: expr.Range().Ptr(),
68 })
69 return -1, true, diags
70 case !countVal.IsKnown():
71 return 0, false, diags
72 }
73
74 err := gocty.FromCtyValue(countVal, &count)
75 if err != nil {
76 diags = diags.Append(&hcl.Diagnostic{
77 Severity: hcl.DiagError,
78 Summary: "Invalid count argument",
79 Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
80 Subject: expr.Range().Ptr(),
81 })
82 return -1, true, diags
83 }
84 if count < 0 {
85 diags = diags.Append(&hcl.Diagnostic{
86 Severity: hcl.DiagError,
87 Summary: "Invalid count argument",
88 Detail: `The given "count" argument value is unsuitable: negative numbers are not supported.`,
89 Subject: expr.Range().Ptr(),
90 })
91 return -1, true, diags
92 }
93
94 return count, true, diags
95 }
96
97 // fixResourceCountSetTransition is a helper function to fix up the state when a
98 // resource transitions its "count" from being set to unset or vice-versa,
99 // treating a 0-key and a no-key instance as aliases for one another across
100 // the transition.
101 //
102 // The correct time to call this function is in the DynamicExpand method for
103 // a node representing a resource, just after evaluating the count with
104 // evaluateResourceCountExpression, and before any other analysis of the
105 // state such as orphan detection.
106 //
107 // This function calls methods on the given EvalContext to update the current
108 // state in-place, if necessary. It is a no-op if there is no count transition
109 // taking place.
110 //
111 // Since the state is modified in-place, this function must take a writer lock
112 // on the state. The caller must therefore not also be holding a state lock,
113 // or this function will block forever awaiting the lock.
114 func fixResourceCountSetTransition(ctx EvalContext, addr addrs.AbsResource, countEnabled bool) {
115 state := ctx.State()
116 changed := state.MaybeFixUpResourceInstanceAddressForCount(addr, countEnabled)
117 if changed {
118 log.Printf("[TRACE] renamed first %s instance in transient state due to count argument change", addr)
119 }
120 }