github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/eval_count.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl/v2" 7 "github.com/muratcelep/terraform/not-internal/tfdiags" 8 "github.com/zclconf/go-cty/cty" 9 "github.com/zclconf/go-cty/cty/gocty" 10 ) 11 12 // evaluateCountExpression is our standard mechanism for interpreting an 13 // expression given for a "count" argument on a resource or a module. This 14 // should be called during expansion in order to determine the final count 15 // value. 16 // 17 // evaluateCountExpression differs from evaluateCountExpressionValue by 18 // returning an error if the count value is not known, and converting the 19 // cty.Value to an integer. 20 func evaluateCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) { 21 countVal, diags := evaluateCountExpressionValue(expr, ctx) 22 if !countVal.IsKnown() { 23 // Currently this is a rather bad outcome from a UX standpoint, since we have 24 // no real mechanism to deal with this situation and all we can do is produce 25 // an error message. 26 // FIXME: In future, implement a built-in mechanism for deferring changes that 27 // can't yet be predicted, and use it to guide the user through several 28 // plan/apply steps until the desired configuration is eventually reached. 29 diags = diags.Append(&hcl.Diagnostic{ 30 Severity: hcl.DiagError, 31 Summary: "Invalid count argument", 32 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.`, 33 Subject: expr.Range().Ptr(), 34 }) 35 } 36 37 if countVal.IsNull() || !countVal.IsKnown() { 38 return -1, diags 39 } 40 41 count, _ := countVal.AsBigFloat().Int64() 42 return int(count), diags 43 } 44 45 // evaluateCountExpressionValue is like evaluateCountExpression 46 // except that it returns a cty.Value which must be a cty.Number and can be 47 // unknown. 48 func evaluateCountExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Value, tfdiags.Diagnostics) { 49 var diags tfdiags.Diagnostics 50 nullCount := cty.NullVal(cty.Number) 51 if expr == nil { 52 return nullCount, nil 53 } 54 55 countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil) 56 diags = diags.Append(countDiags) 57 if diags.HasErrors() { 58 return nullCount, diags 59 } 60 61 // Unmark the count value, sensitive values are allowed in count but not for_each, 62 // as using it here will not disclose the sensitive value 63 countVal, _ = countVal.Unmark() 64 65 switch { 66 case countVal.IsNull(): 67 diags = diags.Append(&hcl.Diagnostic{ 68 Severity: hcl.DiagError, 69 Summary: "Invalid count argument", 70 Detail: `The given "count" argument value is null. An integer is required.`, 71 Subject: expr.Range().Ptr(), 72 }) 73 return nullCount, diags 74 75 case !countVal.IsKnown(): 76 return cty.UnknownVal(cty.Number), diags 77 } 78 79 var count int 80 err := gocty.FromCtyValue(countVal, &count) 81 if err != nil { 82 diags = diags.Append(&hcl.Diagnostic{ 83 Severity: hcl.DiagError, 84 Summary: "Invalid count argument", 85 Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), 86 Subject: expr.Range().Ptr(), 87 }) 88 return nullCount, diags 89 } 90 if count < 0 { 91 diags = diags.Append(&hcl.Diagnostic{ 92 Severity: hcl.DiagError, 93 Summary: "Invalid count argument", 94 Detail: `The given "count" argument value is unsuitable: negative numbers are not supported.`, 95 Subject: expr.Range().Ptr(), 96 }) 97 return nullCount, diags 98 } 99 100 return countVal, diags 101 }