2020-11-20 18:03:11 -06:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
"strings"
|
2020-11-20 18:03:11 -06:00
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
|
|
"github.com/hashicorp/terraform/internal/configs"
|
|
|
|
"github.com/hashicorp/terraform/internal/instances"
|
|
|
|
"github.com/hashicorp/terraform/internal/lang"
|
2022-03-11 10:09:28 -06:00
|
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
2022-03-28 06:24:28 -05:00
|
|
|
"github.com/hashicorp/terraform/internal/plans"
|
2020-11-20 18:03:11 -06:00
|
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
|
|
)
|
|
|
|
|
|
|
|
// evalCheckRules ensures that all of the given check rules pass against
|
|
|
|
// the given HCL evaluation context.
|
|
|
|
//
|
|
|
|
// If any check rules produce an unknown result then they will be silently
|
|
|
|
// ignored on the assumption that the same checks will be run again later
|
|
|
|
// with fewer unknown values in the EvalContext.
|
|
|
|
//
|
|
|
|
// If any of the rules do not pass, the returned diagnostics will contain
|
|
|
|
// errors. Otherwise, it will either be empty or contain only warnings.
|
2022-03-28 06:24:28 -05:00
|
|
|
func evalCheckRules(typ addrs.CheckType, rules []*configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData, diagSeverity tfdiags.Severity) tfdiags.Diagnostics {
|
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
|
2020-11-20 18:03:11 -06:00
|
|
|
if len(rules) == 0 {
|
|
|
|
// Nothing to do
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-03-09 15:04:01 -06:00
|
|
|
severity := diagSeverity.ToHCL()
|
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
for i, rule := range rules {
|
|
|
|
checkAddr := self.Check(typ, i)
|
2020-11-20 18:03:11 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
conditionResult, ruleDiags := evalCheckRule(typ, rule, ctx, self, keyData, severity)
|
|
|
|
diags = diags.Append(ruleDiags)
|
|
|
|
ctx.Conditions().SetResult(checkAddr, conditionResult)
|
|
|
|
}
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
return diags
|
|
|
|
}
|
2020-11-20 18:03:11 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
func evalCheckRule(typ addrs.CheckType, rule *configs.CheckRule, ctx EvalContext, self addrs.Checkable, keyData instances.RepetitionData, severity hcl.DiagnosticSeverity) (*plans.ConditionResult, tfdiags.Diagnostics) {
|
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
const errInvalidCondition = "Invalid condition result"
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
refs, moreDiags := lang.ReferencesInExpr(rule.Condition)
|
|
|
|
diags = diags.Append(moreDiags)
|
|
|
|
moreRefs, moreDiags := lang.ReferencesInExpr(rule.ErrorMessage)
|
|
|
|
diags = diags.Append(moreDiags)
|
|
|
|
refs = append(refs, moreRefs...)
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
conditionResult := &plans.ConditionResult{
|
|
|
|
Address: self,
|
|
|
|
Result: cty.UnknownVal(cty.Bool),
|
|
|
|
Type: typ,
|
|
|
|
}
|
2020-11-20 18:03:11 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
var selfReference addrs.Referenceable
|
|
|
|
// Only resource postconditions can refer to self
|
|
|
|
if typ == addrs.ResourcePostcondition {
|
|
|
|
switch s := self.(type) {
|
|
|
|
case addrs.AbsResourceInstance:
|
|
|
|
selfReference = s.Resource
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Invalid self reference type %t", self))
|
2020-11-20 18:03:11 -06:00
|
|
|
}
|
2022-03-28 06:24:28 -05:00
|
|
|
}
|
|
|
|
scope := ctx.EvaluationScope(selfReference, keyData)
|
2020-11-20 18:03:11 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
hclCtx, moreDiags := scope.EvalContext(refs)
|
|
|
|
diags = diags.Append(moreDiags)
|
|
|
|
|
|
|
|
result, hclDiags := rule.Condition.Value(hclCtx)
|
|
|
|
diags = diags.Append(hclDiags)
|
|
|
|
|
|
|
|
errorValue, errorDiags := rule.ErrorMessage.Value(hclCtx)
|
|
|
|
diags = diags.Append(errorDiags)
|
|
|
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
log.Printf("[TRACE] evalCheckRule: %s: %s", typ, diags.Err().Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !result.IsKnown() {
|
|
|
|
// We'll wait until we've learned more, then.
|
|
|
|
return conditionResult, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
if result.IsNull() {
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
Severity: severity,
|
|
|
|
Summary: errInvalidCondition,
|
|
|
|
Detail: "Condition expression must return either true or false, not null.",
|
|
|
|
Subject: rule.Condition.Range().Ptr(),
|
|
|
|
Expression: rule.Condition,
|
|
|
|
EvalContext: hclCtx,
|
|
|
|
})
|
|
|
|
conditionResult.Result = cty.False
|
|
|
|
conditionResult.ErrorMessage = "Condition expression must return either true or false, not null."
|
|
|
|
return conditionResult, diags
|
|
|
|
}
|
|
|
|
var err error
|
|
|
|
result, err = convert.Convert(result, cty.Bool)
|
|
|
|
if err != nil {
|
|
|
|
detail := fmt.Sprintf("Invalid condition result value: %s.", tfdiags.FormatError(err))
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
Severity: severity,
|
|
|
|
Summary: errInvalidCondition,
|
|
|
|
Detail: detail,
|
|
|
|
Subject: rule.Condition.Range().Ptr(),
|
|
|
|
Expression: rule.Condition,
|
|
|
|
EvalContext: hclCtx,
|
|
|
|
})
|
|
|
|
conditionResult.Result = cty.False
|
|
|
|
conditionResult.ErrorMessage = detail
|
|
|
|
return conditionResult, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
// The condition result may be marked if the expression refers to a
|
|
|
|
// sensitive value.
|
|
|
|
result, _ = result.Unmark()
|
|
|
|
conditionResult.Result = result
|
|
|
|
|
|
|
|
if result.True() {
|
|
|
|
return conditionResult, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
var errorMessage string
|
|
|
|
if !errorDiags.HasErrors() && errorValue.IsKnown() && !errorValue.IsNull() {
|
2020-11-20 18:03:11 -06:00
|
|
|
var err error
|
2022-03-28 06:24:28 -05:00
|
|
|
errorValue, err = convert.Convert(errorValue, cty.String)
|
2020-11-20 18:03:11 -06:00
|
|
|
if err != nil {
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
2022-03-09 15:04:01 -06:00
|
|
|
Severity: severity,
|
2022-03-28 06:24:28 -05:00
|
|
|
Summary: "Invalid error message",
|
|
|
|
Detail: fmt.Sprintf("Unsuitable value for error message: %s.", tfdiags.FormatError(err)),
|
|
|
|
Subject: rule.ErrorMessage.Range().Ptr(),
|
|
|
|
Expression: rule.ErrorMessage,
|
2020-11-20 18:03:11 -06:00
|
|
|
EvalContext: hclCtx,
|
|
|
|
})
|
2022-03-28 06:24:28 -05:00
|
|
|
} else {
|
|
|
|
if marks.Has(errorValue, marks.Sensitive) {
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
Severity: severity,
|
2020-11-20 18:03:11 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
Summary: "Error message refers to sensitive values",
|
|
|
|
Detail: `The error expression used to explain this condition refers to sensitive values. Terraform will not display the resulting message.
|
2022-03-11 10:09:28 -06:00
|
|
|
|
2022-03-28 06:24:28 -05:00
|
|
|
You can correct this by removing references to sensitive values, or by carefully using the nonsensitive() function if the expression will not reveal the sensitive data.`,
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
|
|
|
|
Subject: rule.ErrorMessage.Range().Ptr(),
|
|
|
|
Expression: rule.ErrorMessage,
|
|
|
|
EvalContext: hclCtx,
|
|
|
|
})
|
2022-03-28 06:24:28 -05:00
|
|
|
errorMessage = "The error message included a sensitive value, so it will not be displayed."
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
} else {
|
2022-03-28 06:24:28 -05:00
|
|
|
errorMessage = strings.TrimSpace(errorValue.AsString())
|
core: Check rule error message expressions
Error messages for preconditions, postconditions, and custom variable
validations have until now been string literals. This commit changes
this to treat the field as an HCL expression, which must evaluate to a
string. Most commonly this will either be a string literal or a template
expression.
When the check rule condition is evaluated, we also evaluate the error
message. This means that the error message should always evaluate to a
string value, even if the condition passes. If it does not, this will
result in an error diagnostic.
If the condition fails, and the error message also fails to evaluate, we
fall back to a default error message. This means that the check rule
failure will still be reported, alongside diagnostics explaining why the
custom error message failed to render.
As part of this change, we also necessarily remove the heuristic about
the error message format. This guidance can be readded in future as part
of a configuration hint system.
2022-02-03 13:14:21 -06:00
|
|
|
}
|
|
|
|
}
|
2020-11-20 18:03:11 -06:00
|
|
|
}
|
2022-03-28 06:24:28 -05:00
|
|
|
if errorMessage == "" {
|
|
|
|
errorMessage = "Failed to evaluate condition error message."
|
|
|
|
}
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
Severity: severity,
|
|
|
|
Summary: fmt.Sprintf("%s failed", typ.Description()),
|
|
|
|
Detail: errorMessage,
|
|
|
|
Subject: rule.Condition.Range().Ptr(),
|
|
|
|
Expression: rule.Condition,
|
|
|
|
EvalContext: hclCtx,
|
|
|
|
})
|
|
|
|
conditionResult.ErrorMessage = errorMessage
|
|
|
|
return conditionResult, diags
|
2020-11-20 18:03:11 -06:00
|
|
|
}
|