handle sensitivity in the OutputChange

The state is not loaded here with any marks, so we cannot rely on marks
alone for equality comparison. Compare both the state and the
configuration sensitivity before creating the OutputChange.
This commit is contained in:
James Bardin 2020-10-12 12:10:01 -04:00
parent d2514a9abd
commit 28e4281674
2 changed files with 28 additions and 5 deletions

View File

@ -358,11 +358,13 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error
// if this is a root module, try to get a before value from the state for
// the diff
sensitiveBefore := false
before := cty.NullVal(cty.DynamicPseudoType)
mod := state.Module(n.Addr.Module)
if n.Addr.Module.IsRoot() && mod != nil {
for name, o := range mod.OutputValues {
if name == n.Addr.OutputValue.Name {
sensitiveBefore = o.Sensitive
before = o.Value
break
}
@ -372,7 +374,8 @@ func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error
changes := ctx.Changes()
if changes != nil {
change := &plans.OutputChange{
Addr: n.Addr,
Addr: n.Addr,
Sensitive: sensitiveBefore,
Change: plans.Change{
Action: plans.Delete,
Before: before,
@ -413,32 +416,51 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C
if changes != nil {
// if this is a root module, try to get a before value from the state for
// the diff
sensitiveBefore := false
before := cty.NullVal(cty.DynamicPseudoType)
mod := state.Module(n.Addr.Module)
if n.Addr.Module.IsRoot() && mod != nil {
for name, o := range mod.OutputValues {
if name == n.Addr.OutputValue.Name {
before = o.Value
sensitiveBefore = o.Sensitive
break
}
}
}
// We will not show the value is either the before or after are marked
// as sensitivity. We can show the value again once sensitivity is
// removed from both the config and the state.
sensitiveChange := sensitiveBefore || n.Config.Sensitive
// strip any marks here just to be sure we don't panic on the True comparison
val, _ = val.UnmarkDeep()
var action plans.Action
switch {
case val.IsNull():
action = plans.Delete
case before.IsNull():
action = plans.Create
case val.IsWhollyKnown() && val.Equals(before).True():
case val.IsWhollyKnown() &&
val.Equals(before).True() &&
n.Config.Sensitive == sensitiveBefore:
// Sensitivity must also match to be a NoOp.
// Theoretically marks may not match here, but sensitivity is the
// only one we can act on, and the state will have been loaded
// without any marks to consider.
action = plans.NoOp
default:
action = plans.Update
}
change := &plans.OutputChange{
Addr: n.Addr,
Sensitive: n.Config.Sensitive,
Sensitive: sensitiveChange,
Change: plans.Change{
Action: action,
Before: before,

View File

@ -47,8 +47,6 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error {
// into NodeApplyableOutputs to reflect possible expansion
// through the presence of "count" or "for_each" on the modules.
// if this is a root output, we add the apply or destroy node directly, as
// the root modules does not expand
var changes []*plans.OutputChangeSrc
if t.Changes != nil {
changes = t.Changes.Outputs
@ -69,6 +67,9 @@ func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error {
destroy = rootChange.Action == plans.Delete
}
// If this is a root output, we add the apply or destroy node directly,
// as the root modules does not expand.
var node dag.Vertex
switch {
case c.Path.IsRoot() && destroy: