configs: support ignore_changes wildcards
The initial pass of implementation here missed the special case where ignore_changes can, in the old parser, be set to ["*"] to ignore changes to all attributes. Since that syntax is awkward and non-obvious, our new decoder will instead expect ignore_changes = all, using HCL2's capability to interpret an expression as a literal keyword. For compatibility with old configurations we will still accept the ["*"] form but emit a deprecation warning to encourage moving to the new form.
This commit is contained in:
parent
c5f5340b15
commit
4fa8c16ead
|
@ -3,6 +3,7 @@ package configs
|
|||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -79,3 +80,20 @@ func shimTraversalInString(expr hcl.Expression, wantKeyword bool) (hcl.Expressio
|
|||
SrcRange: srcRange,
|
||||
}, diags
|
||||
}
|
||||
|
||||
// shimIsIgnoreChangesStar returns true if the given expression seems to be
|
||||
// a string literal whose value is "*". This is used to support a legacy
|
||||
// form of ignore_changes = all .
|
||||
//
|
||||
// This function does not itself emit any diagnostics, so it's the caller's
|
||||
// responsibility to emit a warning diagnostic when this function returns true.
|
||||
func shimIsIgnoreChangesStar(expr hcl.Expression) bool {
|
||||
val, valDiags := expr.Value(nil)
|
||||
if valDiags.HasErrors() {
|
||||
return false
|
||||
}
|
||||
if val.Type() != cty.String || val.IsNull() || !val.IsKnown() {
|
||||
return false
|
||||
}
|
||||
return val.AsString() == "*"
|
||||
}
|
||||
|
|
|
@ -129,6 +129,16 @@ func TestParserLoadConfigFileFailureMessages(t *testing.T) {
|
|||
hcl.DiagWarning,
|
||||
"Quoted references are deprecated",
|
||||
},
|
||||
{
|
||||
"valid-files/resources-ignorechanges-all-legacy.tf",
|
||||
hcl.DiagWarning,
|
||||
"Deprecated ignore_changes wildcard",
|
||||
},
|
||||
{
|
||||
"valid-files/resources-ignorechanges-all-legacy.tf.json",
|
||||
hcl.DiagWarning,
|
||||
"Deprecated ignore_changes wildcard",
|
||||
},
|
||||
{
|
||||
"valid-files/resources-provisioner-when-quoted.tf",
|
||||
hcl.DiagWarning,
|
||||
|
|
|
@ -26,6 +26,7 @@ type ManagedResource struct {
|
|||
CreateBeforeDestroy bool
|
||||
PreventDestroy bool
|
||||
IgnoreChanges []hcl.Traversal
|
||||
IgnoreAllChanges bool
|
||||
|
||||
CreateBeforeDestroySet bool
|
||||
PreventDestroySet bool
|
||||
|
@ -118,19 +119,66 @@ func decodeResourceBlock(block *hcl.Block) (*ManagedResource, hcl.Diagnostics) {
|
|||
}
|
||||
|
||||
if attr, exists := lcContent.Attributes["ignore_changes"]; exists {
|
||||
exprs, listDiags := hcl.ExprList(attr.Expr)
|
||||
diags = append(diags, listDiags...)
|
||||
|
||||
for _, expr := range exprs {
|
||||
expr, shimDiags := shimTraversalInString(expr, false)
|
||||
diags = append(diags, shimDiags...)
|
||||
// ignore_changes can either be a list of relative traversals
|
||||
// or it can be just the keyword "all" to ignore changes to this
|
||||
// resource entirely.
|
||||
// ignore_changes = [ami, instance_type]
|
||||
// ignore_changes = all
|
||||
// We also allow two legacy forms for compatibility with earlier
|
||||
// versions:
|
||||
// ignore_changes = ["ami", "instance_type"]
|
||||
// ignore_changes = ["*"]
|
||||
|
||||
traversal, travDiags := hcl.RelTraversalForExpr(expr)
|
||||
diags = append(diags, travDiags...)
|
||||
if len(traversal) != 0 {
|
||||
r.IgnoreChanges = append(r.IgnoreChanges, traversal)
|
||||
kw := hcl.ExprAsKeyword(attr.Expr)
|
||||
|
||||
switch {
|
||||
case kw == "all":
|
||||
r.IgnoreAllChanges = true
|
||||
default:
|
||||
exprs, listDiags := hcl.ExprList(attr.Expr)
|
||||
diags = append(diags, listDiags...)
|
||||
|
||||
var ignoreAllRange hcl.Range
|
||||
|
||||
for _, expr := range exprs {
|
||||
|
||||
// our expr might be the literal string "*", which
|
||||
// we accept as a deprecated way of saying "all".
|
||||
if shimIsIgnoreChangesStar(expr) {
|
||||
r.IgnoreAllChanges = true
|
||||
ignoreAllRange = expr.Range()
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagWarning,
|
||||
Summary: "Deprecated ignore_changes wildcard",
|
||||
Detail: "The [\"*\"] form of ignore_changes wildcard is reprecated. Use \"ignore_changes = all\" to ignore changes to all attributes.",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
expr, shimDiags := shimTraversalInString(expr, false)
|
||||
diags = append(diags, shimDiags...)
|
||||
|
||||
traversal, travDiags := hcl.RelTraversalForExpr(expr)
|
||||
diags = append(diags, travDiags...)
|
||||
if len(traversal) != 0 {
|
||||
r.IgnoreChanges = append(r.IgnoreChanges, traversal)
|
||||
}
|
||||
}
|
||||
|
||||
if r.IgnoreAllChanges && len(r.IgnoreChanges) != 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid ignore_changes ruleset",
|
||||
Detail: "Cannot mix wildcard string \"*\" with non-wildcard references.",
|
||||
Subject: &ignoreAllRange,
|
||||
Context: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case "connection":
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
resource "aws_instance" "web" {
|
||||
lifecycle {
|
||||
ignore_changes = ["*", "foo"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
resource "aws_instance" "web" {
|
||||
lifecycle {
|
||||
ignore_changes = ["*"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"web": {
|
||||
"lifecycle": {
|
||||
"ignore_changes": ["*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
resource "aws_instance" "web" {
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"web": {
|
||||
"lifecycle": {
|
||||
"ignore_changes": "all"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue