]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - 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
index 2ae56a751cfda0ee58b396ff344c3d29057ad6b3..8083105b0e5202270aa09f00801c47b9ffe9fef0 100644 (file)
 package terraform
 
 import (
-       "github.com/hashicorp/terraform/config"
+       "fmt"
+       "log"
+
+       "github.com/hashicorp/hcl2/hcl"
+       "github.com/hashicorp/terraform/addrs"
+       "github.com/hashicorp/terraform/tfdiags"
+       "github.com/zclconf/go-cty/cty"
+       "github.com/zclconf/go-cty/cty/gocty"
 )
 
-// EvalCountFixZeroOneBoundary is an EvalNode that fixes up the state
-// when there is a resource count with zero/one boundary, i.e. fixing
-// a resource named "aws_instance.foo" to "aws_instance.foo.0" and vice-versa.
-type EvalCountFixZeroOneBoundary struct {
-       Resource *config.Resource
+// evaluateResourceCountExpression is our standard mechanism for interpreting an
+// expression given for a "count" argument on a resource. This should be called
+// from the DynamicExpand of a node representing a resource in order to
+// determine the final count value.
+//
+// If the result is zero or positive and no error diagnostics are returned, then
+// the result is the literal count value to use.
+//
+// If the result is -1, this indicates that the given expression is nil and so
+// the "count" behavior should not be enabled for this resource at all.
+//
+// If error diagnostics are returned then the result is always the meaningless
+// placeholder value -1.
+func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) {
+       count, known, diags := evaluateResourceCountExpressionKnown(expr, ctx)
+       if !known {
+               // Currently this is a rather bad outcome from a UX standpoint, since we have
+               // no real mechanism to deal with this situation and all we can do is produce
+               // an error message.
+               // FIXME: In future, implement a built-in mechanism for deferring changes that
+               // can't yet be predicted, and use it to guide the user through several
+               // plan/apply steps until the desired configuration is eventually reached.
+               diags = diags.Append(&hcl.Diagnostic{
+                       Severity: hcl.DiagError,
+                       Summary:  "Invalid count argument",
+                       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.`,
+                       Subject:  expr.Range().Ptr(),
+               })
+       }
+       return count, diags
 }
 
-// TODO: test
-func (n *EvalCountFixZeroOneBoundary) Eval(ctx EvalContext) (interface{}, error) {
-       // Get the count, important for knowing whether we're supposed to
-       // be adding the zero, or trimming it.
-       count, err := n.Resource.Count()
-       if err != nil {
-               return nil, err
+// evaluateResourceCountExpressionKnown is like evaluateResourceCountExpression
+// except that it handles an unknown result by returning count = 0 and
+// a known = false, rather than by reporting the unknown value as an error
+// diagnostic.
+func evaluateResourceCountExpressionKnown(expr hcl.Expression, ctx EvalContext) (count int, known bool, diags tfdiags.Diagnostics) {
+       if expr == nil {
+               return -1, true, nil
        }
 
-       // Figure what to look for and what to replace it with
-       hunt := n.Resource.Id()
-       replace := hunt + ".0"
-       if count < 2 {
-               hunt, replace = replace, hunt
+       countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
+       diags = diags.Append(countDiags)
+       if diags.HasErrors() {
+               return -1, true, diags
        }
 
-       state, lock := ctx.State()
-
-       // Get a lock so we can access this instance and potentially make
-       // changes to it.
-       lock.Lock()
-       defer lock.Unlock()
-
-       // Look for the module state. If we don't have one, then it doesn't matter.
-       mod := state.ModuleByPath(ctx.Path())
-       if mod == nil {
-               return nil, nil
+       switch {
+       case countVal.IsNull():
+               diags = diags.Append(&hcl.Diagnostic{
+                       Severity: hcl.DiagError,
+                       Summary:  "Invalid count argument",
+                       Detail:   `The given "count" argument value is null. An integer is required.`,
+                       Subject:  expr.Range().Ptr(),
+               })
+               return -1, true, diags
+       case !countVal.IsKnown():
+               return 0, false, diags
        }
 
-       // Look for the resource state. If we don't have one, then it is okay.
-       rs, ok := mod.Resources[hunt]
-       if !ok {
-               return nil, nil
+       err := gocty.FromCtyValue(countVal, &count)
+       if err != nil {
+               diags = diags.Append(&hcl.Diagnostic{
+                       Severity: hcl.DiagError,
+                       Summary:  "Invalid count argument",
+                       Detail:   fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
+                       Subject:  expr.Range().Ptr(),
+               })
+               return -1, true, diags
        }
-
-       // If the replacement key exists, we just keep both
-       if _, ok := mod.Resources[replace]; ok {
-               return nil, nil
+       if count < 0 {
+               diags = diags.Append(&hcl.Diagnostic{
+                       Severity: hcl.DiagError,
+                       Summary:  "Invalid count argument",
+                       Detail:   `The given "count" argument value is unsuitable: negative numbers are not supported.`,
+                       Subject:  expr.Range().Ptr(),
+               })
+               return -1, true, diags
        }
 
-       mod.Resources[replace] = rs
-       delete(mod.Resources, hunt)
+       return count, true, diags
+}
 
-       return nil, nil
+// fixResourceCountSetTransition is a helper function to fix up the state when a
+// resource transitions its "count" from being set to unset or vice-versa,
+// treating a 0-key and a no-key instance as aliases for one another across
+// the transition.
+//
+// The correct time to call this function is in the DynamicExpand method for
+// a node representing a resource, just after evaluating the count with
+// evaluateResourceCountExpression, and before any other analysis of the
+// state such as orphan detection.
+//
+// This function calls methods on the given EvalContext to update the current
+// state in-place, if necessary. It is a no-op if there is no count transition
+// taking place.
+//
+// Since the state is modified in-place, this function must take a writer lock
+// on the state. The caller must therefore not also be holding a state lock,
+// or this function will block forever awaiting the lock.
+func fixResourceCountSetTransition(ctx EvalContext, addr addrs.AbsResource, countEnabled bool) {
+       state := ctx.State()
+       changed := state.MaybeFixUpResourceInstanceAddressForCount(addr, countEnabled)
+       if changed {
+               log.Printf("[TRACE] renamed first %s instance in transient state due to count argument change", addr)
+       }
 }