Merge pull request #30462 from hashicorp/alisdair/preconditions-postconditions-self-references
configs: Validate pre/postcondition self-refs
This commit is contained in:
commit
eea706aaef
|
@ -6,6 +6,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/hcl/v2/gohcl"
|
"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,
|
// CheckRule represents a configuration-defined validation rule, precondition,
|
||||||
|
@ -32,6 +34,37 @@ type CheckRule struct {
|
||||||
DeclRange hcl.Range
|
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.
|
// 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
|
// 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":
|
case "precondition", "postcondition":
|
||||||
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
|
moreDiags = cr.validateSelfReferences(block.Type, r.Addr())
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "precondition":
|
case "precondition":
|
||||||
r.Preconditions = append(r.Preconditions, cr)
|
r.Preconditions = append(r.Preconditions, cr)
|
||||||
|
@ -445,6 +449,10 @@ func decodeDataBlock(block *hcl.Block, override bool) (*Resource, hcl.Diagnostic
|
||||||
case "precondition", "postcondition":
|
case "precondition", "postcondition":
|
||||||
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
cr, moreDiags := decodeCheckRuleBlock(block, override)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
|
moreDiags = cr.validateSelfReferences(block.Type, r.Addr())
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "precondition":
|
case "precondition":
|
||||||
r.Preconditions = append(r.Preconditions, cr)
|
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