mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
8b4c89bdaf
Evaluate the expressions stored in replace_triggered_by into the *addrs.Reference needed to lookup changes in the plan.
144 lines
4.4 KiB
Go
144 lines
4.4 KiB
Go
package terraform
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/instances"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func evalReplaceTriggeredByExpr(expr hcl.Expression, keyData instances.RepetitionData) (*addrs.Reference, tfdiags.Diagnostics) {
|
|
var ref *addrs.Reference
|
|
var diags tfdiags.Diagnostics
|
|
|
|
traversal, diags := triggersExprToTraversal(expr, keyData)
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
|
|
// We now have a static traversal, so we can just turn it into an addrs.Reference.
|
|
ref, ds := addrs.ParseRef(traversal)
|
|
diags = diags.Append(ds)
|
|
|
|
return ref, diags
|
|
}
|
|
|
|
// trggersExprToTraversal takes an hcl expression limited to the syntax allowed
|
|
// in replace_triggered_by, and converts it to a static traversal. The
|
|
// RepetitionData contains the data necessary to evaluate the only allowed
|
|
// variables in the expression, count.index and each.key.
|
|
func triggersExprToTraversal(expr hcl.Expression, keyData instances.RepetitionData) (hcl.Traversal, tfdiags.Diagnostics) {
|
|
var trav hcl.Traversal
|
|
var diags tfdiags.Diagnostics
|
|
|
|
switch e := expr.(type) {
|
|
case *hclsyntax.RelativeTraversalExpr:
|
|
t, d := triggersExprToTraversal(e.Source, keyData)
|
|
diags = diags.Append(d)
|
|
trav = append(trav, t...)
|
|
trav = append(trav, e.Traversal...)
|
|
|
|
case *hclsyntax.ScopeTraversalExpr:
|
|
// a static reference, we can just append the traversal
|
|
trav = append(trav, e.Traversal...)
|
|
|
|
case *hclsyntax.IndexExpr:
|
|
// Get the collection from the index expression
|
|
t, d := triggersExprToTraversal(e.Collection, keyData)
|
|
diags = diags.Append(d)
|
|
if diags.HasErrors() {
|
|
return nil, diags
|
|
}
|
|
trav = append(trav, t...)
|
|
|
|
// The index key is the only place where we could have variables that
|
|
// reference count and each, so we need to parse those independently.
|
|
idx, hclDiags := parseIndexKeyExpr(e.Key, keyData)
|
|
diags = diags.Append(hclDiags)
|
|
|
|
trav = append(trav, idx)
|
|
|
|
default:
|
|
// Something unexpected got through config validation. We're not sure
|
|
// what it is, but we'll point it out in the diagnostics for the user
|
|
// to fix.
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid replace_triggered_by expression",
|
|
Detail: "Unexpected expression found in replace_triggered_by.",
|
|
Subject: e.Range().Ptr(),
|
|
})
|
|
}
|
|
|
|
return trav, diags
|
|
}
|
|
|
|
// parseIndexKeyExpr takes an hcl.Expression and parses it as an index key, while
|
|
// evaluating any references to count.index or each.key.
|
|
func parseIndexKeyExpr(expr hcl.Expression, keyData instances.RepetitionData) (hcl.TraverseIndex, hcl.Diagnostics) {
|
|
idx := hcl.TraverseIndex{
|
|
SrcRange: expr.Range(),
|
|
}
|
|
|
|
trav, diags := hcl.RelTraversalForExpr(expr)
|
|
if diags.HasErrors() {
|
|
return idx, diags
|
|
}
|
|
|
|
keyParts := []string{}
|
|
|
|
for _, t := range trav {
|
|
attr, ok := t.(hcl.TraverseAttr)
|
|
if !ok {
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid index expression",
|
|
Detail: "Only constant values, count.index or each.key are allowed in index expressions.",
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
return idx, diags
|
|
}
|
|
keyParts = append(keyParts, attr.Name)
|
|
}
|
|
|
|
switch strings.Join(keyParts, ".") {
|
|
case "count.index":
|
|
if keyData.CountIndex == cty.NilVal {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: `Reference to "count" in non-counted context`,
|
|
Detail: `The "count" object can only be used in "resource" blocks when the "count" argument is set.`,
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
}
|
|
idx.Key = keyData.CountIndex
|
|
|
|
case "each.key":
|
|
if keyData.EachKey == cty.NilVal {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: `Reference to "each" in context without for_each`,
|
|
Detail: `The "each" object can be used only in "resource" blocks when the "for_each" argument is set.`,
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
}
|
|
idx.Key = keyData.EachKey
|
|
default:
|
|
// Something may have slipped through validation, probably from a json
|
|
// configuration.
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid index expression",
|
|
Detail: "Only constant values, count.index or each.key are allowed in index expressions.",
|
|
Subject: expr.Range().Ptr(),
|
|
})
|
|
}
|
|
|
|
return idx, diags
|
|
|
|
}
|