configs: Validate pre/postcondition self-refs
Preconditions and postconditions for resources and data sources may not refer to the address of the containing resource or data source. This commit adds a parse-time validation for this rule.
This commit is contained in:
parent
0634c9437a
commit
7ded73f266
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/lang"
|
||||
)
|
||||
|
||||
// CheckRule represents a configuration-defined validation rule, precondition,
|
||||
|
@ -32,6 +34,37 @@ type CheckRule struct {
|
|||
DeclRange hcl.Range
|
||||
}
|
||||
|
||||
// validateSelfReferences looks for references in the check rule matching the
|
||||
// specified resource address, returning error diagnostics if such a reference
|
||||
// is found.
|
||||
func (cr *CheckRule) validateSelfReferences(checkType string, addr addrs.Resource) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
refs, _ := lang.References(cr.Condition.Variables())
|
||||
for _, ref := range refs {
|
||||
var refAddr addrs.Resource
|
||||
|
||||
switch rs := ref.Subject.(type) {
|
||||
case addrs.Resource:
|
||||
refAddr = rs
|
||||
case addrs.ResourceInstance:
|
||||
refAddr = rs.Resource
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if refAddr.Equal(addr) {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Invalid reference in %s", checkType),
|
||||
Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addr.String()),
|
||||
Subject: cr.Condition.Range().Ptr(),
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
// decodeCheckRuleBlock decodes the contents of the given block as a check rule.
|
||||
//
|
||||
// Unlike most of our "decode..." functions, this one can be applied to blocks
|
||||
|
|
|
@ -245,6 +245,10 @@ func decodeResourceBlock(block *hcl.Block, override bool) (*Resource, hcl.Diagno
|
|||
case "precondition", "postcondition":
|
||||
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
moreDiags = cr.validateSelfReferences(block.Type, r.Addr())
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
switch block.Type {
|
||||
case "precondition":
|
||||
r.Preconditions = append(r.Preconditions, cr)
|
||||
|
@ -445,6 +449,10 @@ func decodeDataBlock(block *hcl.Block, override bool) (*Resource, hcl.Diagnostic
|
|||
case "precondition", "postcondition":
|
||||
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
moreDiags = cr.validateSelfReferences(block.Type, r.Addr())
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
switch block.Type {
|
||||
case "precondition":
|
||||
r.Preconditions = append(r.Preconditions, cr)
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
resource "test" "test" {
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = test.test.foo # ERROR: Invalid reference in precondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
postcondition {
|
||||
condition = test.test.foo # ERROR: Invalid reference in postcondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "test" "test" {
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = data.test.test.foo # ERROR: Invalid reference in precondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
postcondition {
|
||||
condition = data.test.test.foo # ERROR: Invalid reference in postcondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "test" "test_counted" {
|
||||
count = 1
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = test.test_counted[0].foo # ERROR: Invalid reference in precondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
postcondition {
|
||||
condition = test.test_counted[0].foo # ERROR: Invalid reference in postcondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "test" "test_counted" {
|
||||
count = 1
|
||||
|
||||
lifecycle {
|
||||
precondition {
|
||||
condition = data.test.test_counted[0].foo # ERROR: Invalid reference in precondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
postcondition {
|
||||
condition = data.test.test_counted[0].foo # ERROR: Invalid reference in postcondition
|
||||
error_message = "Cannot refer to self."
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue