core: skip resource validation when count is unknown

The approach here is a little hacky, since this edge case applies only to
validate and all of the other evaluateResourceCountExpression callers
don't care about it: we overload the "count" return value as a flag to
allow NodeValidatableResource to allow it to detect this situation and
silently ignore errors in this case.
This commit is contained in:
Martin Atkins 2018-05-11 17:43:18 -07:00
parent b54342460c
commit 2c1ef35965
2 changed files with 25 additions and 7 deletions

View File

@ -22,8 +22,13 @@ import (
// If the result is -1, this indicates that the given expression is nil and so // 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. // the "count" behavior should not be enabled for this resource at all.
// //
// If error diagnostics are returned then the result is undefined and must // If error diagnostics are returned then the result is always the meaningless
// not be used. // placeholder value -1, except in one case: if the count expression evaluates
// to an unknown number value then the result is zero, allowing this situation
// to be treated by the caller as special if needed. For example, an early
// graph walk may wish to just silently skip resources with unknown counts
// to allow them to be dealt with in a later graph walk where more information
// is available.
func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) { func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) {
if expr == nil { if expr == nil {
return -1, nil return -1, nil
@ -60,7 +65,11 @@ func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int,
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.`, 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(), Subject: expr.Range().Ptr(),
}) })
return -1, diags // We return zero+errors in this one case to allow callers to handle
// an unknown count as special. This is rarely necessary, but is used
// by the validate walk in particular so that it can just skip
// validation in this case, assuming a later walk will take care of it.
return 0, diags
} }
err := gocty.FromCtyValue(countVal, &count) err := gocty.FromCtyValue(countVal, &count)

View File

@ -31,10 +31,19 @@ func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error)
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
diags = diags.Append(countDiags) diags = diags.Append(countDiags)
if countDiags.HasErrors() { if countDiags.HasErrors() {
log.Printf("[TRACE] %T %s: count expression has errors", n, n.Name()) if count != 0 {
return nil, diags.Err() log.Printf("[TRACE] %T %s: count expression has errors", n, n.Name())
} return nil, diags.Err()
if count >= 0 { }
// evaluateResourceCountExpression returns zero+errors only in the
// case where the count value successfully evaluated to an unknown
// number. We don't treat this as an error here because counts that
// are computed during validate can become known during the plan
// walk, if they refer to data resources, and so we'll just defer
// our validation steps to the plan phase in that case.
log.Printf("[TRACE] %T %s: count expression value not yet known, so deferring validation until the plan walk", n, n.Name())
} else if count >= 0 {
log.Printf("[TRACE] %T %s: count expression evaluates to %d", n, n.Name(), count) log.Printf("[TRACE] %T %s: count expression evaluates to %d", n, n.Name(), count)
} else { } else {
log.Printf("[TRACE] %T %s: no count argument present", n, n.Name()) log.Printf("[TRACE] %T %s: no count argument present", n, n.Name())