Skip to content

Commit

Permalink
terraform: Error message for unknown error_message in variable valida…
Browse files Browse the repository at this point in the history
…tion

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.
  • Loading branch information
apparentlymart committed Jul 11, 2024
1 parent 46a6d3f commit eb7f58c
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
17 changes: 16 additions & 1 deletion internal/terraform/eval_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
46 changes: 46 additions & 0 deletions internal/terraform/eval_variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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'")
}
})
}

0 comments on commit eb7f58c

Please sign in to comment.