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 (
|
import (
|
||||||
"github.com/hashicorp/hcl2/hcl"
|
"github.com/hashicorp/hcl2/hcl"
|
||||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
"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,
|
SrcRange: srcRange,
|
||||||
}, diags
|
}, 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,
|
hcl.DiagWarning,
|
||||||
"Quoted references are deprecated",
|
"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",
|
"valid-files/resources-provisioner-when-quoted.tf",
|
||||||
hcl.DiagWarning,
|
hcl.DiagWarning,
|
||||||
|
|
|
@ -26,6 +26,7 @@ type ManagedResource struct {
|
||||||
CreateBeforeDestroy bool
|
CreateBeforeDestroy bool
|
||||||
PreventDestroy bool
|
PreventDestroy bool
|
||||||
IgnoreChanges []hcl.Traversal
|
IgnoreChanges []hcl.Traversal
|
||||||
|
IgnoreAllChanges bool
|
||||||
|
|
||||||
CreateBeforeDestroySet bool
|
CreateBeforeDestroySet bool
|
||||||
PreventDestroySet bool
|
PreventDestroySet bool
|
||||||
|
@ -118,19 +119,66 @@ func decodeResourceBlock(block *hcl.Block) (*ManagedResource, hcl.Diagnostics) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr, exists := lcContent.Attributes["ignore_changes"]; exists {
|
if attr, exists := lcContent.Attributes["ignore_changes"]; exists {
|
||||||
exprs, listDiags := hcl.ExprList(attr.Expr)
|
|
||||||
diags = append(diags, listDiags...)
|
|
||||||
|
|
||||||
for _, expr := range exprs {
|
// ignore_changes can either be a list of relative traversals
|
||||||
expr, shimDiags := shimTraversalInString(expr, false)
|
// or it can be just the keyword "all" to ignore changes to this
|
||||||
diags = append(diags, shimDiags...)
|
// 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)
|
kw := hcl.ExprAsKeyword(attr.Expr)
|
||||||
diags = append(diags, travDiags...)
|
|
||||||
if len(traversal) != 0 {
|
switch {
|
||||||
r.IgnoreChanges = append(r.IgnoreChanges, traversal)
|
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":
|
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