From 64cf53bd7e4e4b660c39fd4c27318b8716fd12cf Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Tue, 21 Jun 2022 12:13:45 -0400 Subject: [PATCH] configs: Fix check block configuration diagnostics When validating self-references for resource and data source preconditions and postconditions, we previously did not nil-check the block's condition field, which caused a panic when the block had no condition. While fixing this I noticed that we were not validating that there are no self-references in the error message, so fixed that. --- internal/configs/checks.go | 47 +++++++++++-------- .../precondition-postcondition-badref.tf | 29 ++++++++++++ ...ndition-postcondition-missing-condition.tf | 12 +++++ 3 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 internal/configs/testdata/invalid-files/precondition-postcondition-badref.tf create mode 100644 internal/configs/testdata/invalid-files/precondition-postcondition-missing-condition.tf diff --git a/internal/configs/checks.go b/internal/configs/checks.go index 10ad62b69a42..417dff45eece 100644 --- a/internal/configs/checks.go +++ b/internal/configs/checks.go @@ -40,27 +40,36 @@ type CheckRule struct { // 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: + exprs := []hcl.Expression{ + cr.Condition, + cr.ErrorMessage, + } + for _, expr := range exprs { + if expr == nil { 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 + refs, _ := lang.References(expr.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: expr.Range().Ptr(), + }) + break + } } } return diags diff --git a/internal/configs/testdata/invalid-files/precondition-postcondition-badref.tf b/internal/configs/testdata/invalid-files/precondition-postcondition-badref.tf new file mode 100644 index 000000000000..ff5ff712962b --- /dev/null +++ b/internal/configs/testdata/invalid-files/precondition-postcondition-badref.tf @@ -0,0 +1,29 @@ +data "example" "example" { + foo = 5 + + lifecycle { + precondition { + condition = data.example.example.foo == 5 # ERROR: Invalid reference in precondition + error_message = "Must be five." + } + postcondition { + condition = self.foo == 5 + error_message = "Must be five, but is ${data.example.example.foo}." # ERROR: Invalid reference in postcondition + } + } +} + +resource "example" "example" { + foo = 5 + + lifecycle { + precondition { + condition = example.example.foo == 5 # ERROR: Invalid reference in precondition + error_message = "Must be five." + } + postcondition { + condition = self.foo == 5 + error_message = "Must be five, but is ${example.example.foo}." # ERROR: Invalid reference in postcondition + } + } +} diff --git a/internal/configs/testdata/invalid-files/precondition-postcondition-missing-condition.tf b/internal/configs/testdata/invalid-files/precondition-postcondition-missing-condition.tf new file mode 100644 index 000000000000..d0a6ab3074da --- /dev/null +++ b/internal/configs/testdata/invalid-files/precondition-postcondition-missing-condition.tf @@ -0,0 +1,12 @@ +resource "example" "example" { + foo = 5 + + lifecycle { + precondition { # ERROR: Missing required argument + error_message = "Can a check block fail without a condition?" + } + postcondition { # ERROR: Missing required argument + error_message = "Do not try to pass the check; only realize that there is no check." + } + } +}