From b0b1f310090beaec063e7a71699990424149ee4d Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 28 Jun 2024 15:50:46 -0700 Subject: [PATCH] terraform: Error message for unknown error_message in variable validation In cases where the "condition" is known but the "error_message" is not, we were previously returning the generic error about the error message not being evaluable. In the long run we'll want to do something better here to minimize the chances of this situation arising -- reporting an error that we don't know how to describe is a poor user experience -- but this is a temporary intervention to at least make the error message more relevant in the meantime, while we design how Terraform actually ought to behave in this awkward situation. --- internal/terraform/eval_variable.go | 17 ++++++++- internal/terraform/eval_variable_test.go | 46 ++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/internal/terraform/eval_variable.go b/internal/terraform/eval_variable.go index 90c50fb1b610..7e62f87ab788 100644 --- a/internal/terraform/eval_variable.go +++ b/internal/terraform/eval_variable.go @@ -427,8 +427,23 @@ func evalVariableValidation(validation *configs.CheckRule, hclCtx *hcl.EvalConte return checkResult{Status: status}, diags } + if !errorValue.IsKnown() { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid error message", + Detail: "Unsuitable value for error message: expression refers to values that won't be known until the apply phase.", + Subject: validation.ErrorMessage.Range().Ptr(), + Expression: validation.ErrorMessage, + EvalContext: hclCtx, + Extra: diagnosticCausedByUnknown(true), + }) + return checkResult{ + Status: checks.StatusError, + }, diags + } + var errorMessage string - if !errorDiags.HasErrors() && errorValue.IsKnown() && !errorValue.IsNull() { + if !errorDiags.HasErrors() && !errorValue.IsNull() { var err error errorValue, err = convert.Convert(errorValue, cty.String) if err != nil { diff --git a/internal/terraform/eval_variable_test.go b/internal/terraform/eval_variable_test.go index 7781b353817c..6ef54353f07b 100644 --- a/internal/terraform/eval_variable_test.go +++ b/internal/terraform/eval_variable_test.go @@ -9,10 +9,12 @@ import ( "testing" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/checks" + "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/lang" "github.com/hashicorp/terraform/internal/lang/marks" "github.com/hashicorp/terraform/internal/namedvals" @@ -1374,3 +1376,47 @@ variable "bar" { }) } } + +func TestEvalVariableValidation_unknownValues(t *testing.T) { + t.Run("known condition, unknown error_message", func(t *testing.T) { + rule := &configs.CheckRule{ + Condition: hcltest.MockExprLiteral(cty.False), + ErrorMessage: hcltest.MockExprLiteral(cty.UnknownVal(cty.String)), + } + hclCtx := &hcl.EvalContext{} + varAddr := addrs.AbsInputVariableInstance{ + Module: addrs.RootModuleInstance, + Variable: addrs.InputVariable{Name: "foo"}, + } + + result, diags := evalVariableValidation(rule, hclCtx, hcl.Range{}, varAddr, 0) + if got, want := result.Status, checks.StatusError; got != want { + t.Errorf("wrong result.Status\ngot: %s\nwant: %s", got, want) + } + if !diags.HasErrors() { + t.Fatalf("unexpected success; want error") + } + found := false + hasCorrectExtra := false + wantDesc := tfdiags.Description{ + Summary: "Invalid error message", + Detail: "Unsuitable value for error message: expression refers to values that won't be known until the apply phase.", + } + for _, diag := range diags { + gotDesc := diag.Description() + if diag.Severity() == tfdiags.Error && gotDesc.Summary == wantDesc.Summary && gotDesc.Detail == wantDesc.Detail { + found = true + hasCorrectExtra = tfdiags.DiagnosticCausedByUnknown(diag) + break + } + } + if !found { + t.Errorf("missing expected error diagnostic\nwant: %s: %s\ngot: %s", + wantDesc.Summary, wantDesc.Detail, + diags.Err().Error(), + ) + } else if !hasCorrectExtra { + t.Errorf("diagnostic is not marked as being 'caused by unknown'") + } + }) +}