handle marks within ignore_changes
Up until now marks were not considered by `ignore_changes`, that however means changes to sensitivity within a configuration cannot ignored, even though they are planned as changes. Rather than separating the marks and tracking their paths, we can easily update the processIgnoreChanges routine to handle the marked values directly. Moving the `processIgnoreChanges` call also cleans up some of the variable naming, making it more consistent through the body of the function.
This commit is contained in:
parent
6d65c19134
commit
ea077cbd90
|
@ -687,26 +687,24 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||||
priorVal = cty.NullVal(schema.ImpliedType())
|
priorVal = cty.NullVal(schema.ImpliedType())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an unmarked version of our config val and our prior val.
|
|
||||||
// Store the paths for the config val to re-markafter
|
|
||||||
// we've sent things over the wire.
|
|
||||||
unmarkedConfigVal, unmarkedPaths := origConfigVal.UnmarkDeepWithPaths()
|
|
||||||
unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths()
|
|
||||||
|
|
||||||
log.Printf("[TRACE] Re-validating config for %q", n.Addr)
|
log.Printf("[TRACE] Re-validating config for %q", n.Addr)
|
||||||
// Allow the provider to validate the final set of values.
|
// Allow the provider to validate the final set of values. The config was
|
||||||
// The config was statically validated early on, but there may have been
|
// statically validated early on, but there may have been unknown values
|
||||||
// unknown values which the provider could not validate at the time.
|
// which the provider could not validate at the time.
|
||||||
|
//
|
||||||
// TODO: It would be more correct to validate the config after
|
// TODO: It would be more correct to validate the config after
|
||||||
// ignore_changes has been applied, but the current implementation cannot
|
// ignore_changes has been applied, but the current implementation cannot
|
||||||
// exclude computed-only attributes when given the `all` option.
|
// exclude computed-only attributes when given the `all` option.
|
||||||
|
|
||||||
|
// we must unmark and use the original config, since the ignore_changes
|
||||||
|
// handling below needs access to the marks.
|
||||||
|
unmarkedConfigVal, _ := origConfigVal.UnmarkDeep()
|
||||||
validateResp := provider.ValidateResourceConfig(
|
validateResp := provider.ValidateResourceConfig(
|
||||||
providers.ValidateResourceConfigRequest{
|
providers.ValidateResourceConfigRequest{
|
||||||
TypeName: n.Addr.Resource.Resource.Type,
|
TypeName: n.Addr.Resource.Resource.Type,
|
||||||
Config: unmarkedConfigVal,
|
Config: unmarkedConfigVal,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
return plan, state, diags
|
return plan, state, diags
|
||||||
|
@ -717,13 +715,21 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||||
// the proposed value, the proposed value itself, and the config presented
|
// the proposed value, the proposed value itself, and the config presented
|
||||||
// to the provider in the PlanResourceChange request all agree on the
|
// to the provider in the PlanResourceChange request all agree on the
|
||||||
// starting values.
|
// starting values.
|
||||||
configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(unmarkedPriorVal, unmarkedConfigVal)
|
// Here we operate on the marked values, so as to revert any changes to the
|
||||||
|
// marks as well as the value.
|
||||||
|
configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(priorVal, origConfigVal)
|
||||||
diags = diags.Append(ignoreChangeDiags)
|
diags = diags.Append(ignoreChangeDiags)
|
||||||
if ignoreChangeDiags.HasErrors() {
|
if ignoreChangeDiags.HasErrors() {
|
||||||
return plan, state, diags
|
return plan, state, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
proposedNewVal := objchange.ProposedNew(schema, unmarkedPriorVal, configValIgnored)
|
// Create an unmarked version of our config val and our prior val.
|
||||||
|
// Store the paths for the config val to re-mark after we've sent things
|
||||||
|
// over the wire.
|
||||||
|
unmarkedConfigVal, unmarkedPaths := configValIgnored.UnmarkDeepWithPaths()
|
||||||
|
unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths()
|
||||||
|
|
||||||
|
proposedNewVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal)
|
||||||
|
|
||||||
// Call pre-diff hook
|
// Call pre-diff hook
|
||||||
diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
|
diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
|
||||||
|
@ -735,7 +741,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||||
|
|
||||||
resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{
|
resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{
|
||||||
TypeName: n.Addr.Resource.Resource.Type,
|
TypeName: n.Addr.Resource.Resource.Type,
|
||||||
Config: configValIgnored,
|
Config: unmarkedConfigVal,
|
||||||
PriorState: unmarkedPriorVal,
|
PriorState: unmarkedPriorVal,
|
||||||
ProposedNewState: proposedNewVal,
|
ProposedNewState: proposedNewVal,
|
||||||
PriorPrivate: priorPrivate,
|
PriorPrivate: priorPrivate,
|
||||||
|
@ -774,7 +780,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||||
return plan, state, diags
|
return plan, state, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, configValIgnored, plannedNewVal); len(errs) > 0 {
|
if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
|
||||||
if resp.LegacyTypeSystem {
|
if resp.LegacyTypeSystem {
|
||||||
// The shimming of the old type system in the legacy SDK is not precise
|
// The shimming of the old type system in the legacy SDK is not precise
|
||||||
// enough to pass this consistency check, so we'll give it a pass here,
|
// enough to pass this consistency check, so we'll give it a pass here,
|
||||||
|
@ -1085,7 +1091,7 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value) (ct
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreChanges := n.Config.Managed.IgnoreChanges
|
ignoreChanges := traversalsToPaths(n.Config.Managed.IgnoreChanges)
|
||||||
ignoreAll := n.Config.Managed.IgnoreAllChanges
|
ignoreAll := n.Config.Managed.IgnoreAllChanges
|
||||||
|
|
||||||
if len(ignoreChanges) == 0 && !ignoreAll {
|
if len(ignoreChanges) == 0 && !ignoreAll {
|
||||||
|
@ -1100,14 +1106,16 @@ func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value) (ct
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return processIgnoreChangesIndividual(prior, config, ignoreChanges)
|
ret, diags := processIgnoreChangesIndividual(prior, config, ignoreChanges)
|
||||||
|
|
||||||
|
return ret, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl.Traversal) (cty.Value, tfdiags.Diagnostics) {
|
// Convert the hcl.Traversal values we get form the configuration to the
|
||||||
// When we walk below we will be using cty.Path values for comparison, so
|
// cty.Path values we need to operate on the cty.Values
|
||||||
// we'll convert our traversals here so we can compare more easily.
|
func traversalsToPaths(traversals []hcl.Traversal) []cty.Path {
|
||||||
ignoreChangesPath := make([]cty.Path, len(ignoreChanges))
|
paths := make([]cty.Path, len(traversals))
|
||||||
for i, traversal := range ignoreChanges {
|
for i, traversal := range traversals {
|
||||||
path := make(cty.Path, len(traversal))
|
path := make(cty.Path, len(traversal))
|
||||||
for si, step := range traversal {
|
for si, step := range traversal {
|
||||||
switch ts := step.(type) {
|
switch ts := step.(type) {
|
||||||
|
@ -1127,9 +1135,12 @@ func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl
|
||||||
panic(fmt.Sprintf("unsupported traversal step %#v", step))
|
panic(fmt.Sprintf("unsupported traversal step %#v", step))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ignoreChangesPath[i] = path
|
paths[i] = path
|
||||||
}
|
}
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChangesPath []cty.Path) (cty.Value, tfdiags.Diagnostics) {
|
||||||
type ignoreChange struct {
|
type ignoreChange struct {
|
||||||
// Path is the full path, minus any trailing map index
|
// Path is the full path, minus any trailing map index
|
||||||
path cty.Path
|
path cty.Path
|
||||||
|
@ -1175,8 +1186,7 @@ func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl
|
||||||
// here even if our ignored key doesn't change. That is OK since it
|
// here even if our ignored key doesn't change. That is OK since it
|
||||||
// won't cause any changes in the transformation, but allows us to skip
|
// won't cause any changes in the transformation, but allows us to skip
|
||||||
// breaking up the maps and checking for key existence here too.
|
// breaking up the maps and checking for key existence here too.
|
||||||
eq := p.Equals(c)
|
if !p.RawEquals(c) {
|
||||||
if !eq.IsKnown() || eq.False() {
|
|
||||||
// there a change to ignore at this path, store the prior value
|
// there a change to ignore at this path, store the prior value
|
||||||
ignoredValues = append(ignoredValues, ignoreChange{icPath, p, key})
|
ignoredValues = append(ignoredValues, ignoreChange{icPath, p, key})
|
||||||
}
|
}
|
||||||
|
@ -1205,6 +1215,10 @@ func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The map values will remain as cty values, so we only need to store
|
||||||
|
// the marks from the outer map itself
|
||||||
|
v, vMarks := v.Unmark()
|
||||||
|
|
||||||
// The configMap is the current configuration value, which we will
|
// The configMap is the current configuration value, which we will
|
||||||
// mutate based on the ignored paths and the prior map value.
|
// mutate based on the ignored paths and the prior map value.
|
||||||
var configMap map[string]cty.Value
|
var configMap map[string]cty.Value
|
||||||
|
@ -1234,11 +1248,17 @@ func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl
|
||||||
// return null for a key with a null value and for a non-existent
|
// return null for a key with a null value and for a non-existent
|
||||||
// key.
|
// key.
|
||||||
var priorMap map[string]cty.Value
|
var priorMap map[string]cty.Value
|
||||||
|
|
||||||
|
// We need to drop the marks from the ignored map for handling. We
|
||||||
|
// don't need to store these, as we now know the ignored value is
|
||||||
|
// only within the map, not the map itself.
|
||||||
|
ignoredVal, _ := ignored.value.Unmark()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case ignored.value.IsNull() || ignored.value.LengthInt() == 0:
|
case ignored.value.IsNull() || ignoredVal.LengthInt() == 0:
|
||||||
priorMap = map[string]cty.Value{}
|
priorMap = map[string]cty.Value{}
|
||||||
default:
|
default:
|
||||||
priorMap = ignored.value.AsValueMap()
|
priorMap = ignoredVal.AsValueMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
key := ignored.key.AsString()
|
key := ignored.key.AsString()
|
||||||
|
@ -1254,11 +1274,18 @@ func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newVal cty.Value
|
||||||
if len(configMap) == 0 {
|
if len(configMap) == 0 {
|
||||||
return cty.MapValEmpty(v.Type().ElementType()), nil
|
newVal = cty.MapValEmpty(v.Type().ElementType())
|
||||||
|
} else {
|
||||||
|
newVal = cty.MapVal(configMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cty.MapVal(configMap), nil
|
if len(vMarks) > 0 {
|
||||||
|
newVal = v.WithMarks(vMarks)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newVal, nil
|
||||||
})
|
})
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ func TestProcessIgnoreChangesIndividual(t *testing.T) {
|
||||||
ignore[i] = trav
|
ignore[i] = trav
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, diags := processIgnoreChangesIndividual(test.Old, test.New, ignore)
|
ret, diags := processIgnoreChangesIndividual(test.Old, test.New, traversalsToPaths(ignore))
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Fatal(diags.Err())
|
t.Fatal(diags.Err())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue