github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/eval_count.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/terraform/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  			// TODO: Also populate Expression and EvalContext in here, but
    36  			// we can't easily do that right now because the hcl.EvalContext
    37  			// (which is not the same as the ctx we have in scope here) is
    38  			// hidden away inside evaluateCountExpressionValue.
    39  			Extra: diagnosticCausedByUnknown(true),
    40  		})
    41  	}
    42  
    43  	if countVal.IsNull() || !countVal.IsKnown() {
    44  		return -1, diags
    45  	}
    46  
    47  	count, _ := countVal.AsBigFloat().Int64()
    48  	return int(count), diags
    49  }
    50  
    51  // evaluateCountExpressionValue is like evaluateCountExpression
    52  // except that it returns a cty.Value which must be a cty.Number and can be
    53  // unknown.
    54  func evaluateCountExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
    55  	var diags tfdiags.Diagnostics
    56  	nullCount := cty.NullVal(cty.Number)
    57  	if expr == nil {
    58  		return nullCount, nil
    59  	}
    60  
    61  	countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil)
    62  	diags = diags.Append(countDiags)
    63  	if diags.HasErrors() {
    64  		return nullCount, diags
    65  	}
    66  
    67  	// Unmark the count value, sensitive values are allowed in count but not for_each,
    68  	// as using it here will not disclose the sensitive value
    69  	countVal, _ = countVal.Unmark()
    70  
    71  	switch {
    72  	case countVal.IsNull():
    73  		diags = diags.Append(&hcl.Diagnostic{
    74  			Severity: hcl.DiagError,
    75  			Summary:  "Invalid count argument",
    76  			Detail:   `The given "count" argument value is null. An integer is required.`,
    77  			Subject:  expr.Range().Ptr(),
    78  		})
    79  		return nullCount, diags
    80  
    81  	case !countVal.IsKnown():
    82  		return cty.UnknownVal(cty.Number), diags
    83  	}
    84  
    85  	var count int
    86  	err := gocty.FromCtyValue(countVal, &count)
    87  	if err != nil {
    88  		diags = diags.Append(&hcl.Diagnostic{
    89  			Severity: hcl.DiagError,
    90  			Summary:  "Invalid count argument",
    91  			Detail:   fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err),
    92  			Subject:  expr.Range().Ptr(),
    93  		})
    94  		return nullCount, diags
    95  	}
    96  	if count < 0 {
    97  		diags = diags.Append(&hcl.Diagnostic{
    98  			Severity: hcl.DiagError,
    99  			Summary:  "Invalid count argument",
   100  			Detail:   `The given "count" argument value is unsuitable: must be greater than or equal to zero.`,
   101  			Subject:  expr.Range().Ptr(),
   102  		})
   103  		return nullCount, diags
   104  	}
   105  
   106  	return countVal, diags
   107  }