Skip to content

Commit

Permalink
plan: indicate when resource deleted due to move
Browse files Browse the repository at this point in the history
Add a new ChangeReason, ReasonDeleteBecauseNoMoveTarget, to provide better
information in cases where a planned deletion is due to moving a resource to
a target not in configuration.

Consider a case in which a resource instance exists in state at address A, and
the user adds a moved block to move A to address B. Whether by the user's
intention or not, address B does not exist in configuration.
Terraform combines the move from A to B, and the lack of configuration for B,
into a single delete action for the (previously nonexistent) entity B.
Prior to this commit, the Terraform plan will report only that resource B will
be destroyed because it does not exist in configuration, with no mention of the
move.

This commit provides the user an additional clue as to what has happened, in a
case in which Terraform has elided a user's action and inaction into one
potentially destructive change.
  • Loading branch information
kmoe committed Aug 26, 2022
1 parent 101d0d9 commit 891b5d5
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 66 deletions.
2 changes: 2 additions & 0 deletions internal/command/format/diff.go
Expand Up @@ -115,6 +115,8 @@ func ResourceChange(
switch change.ActionReason {
case plans.ResourceInstanceDeleteBecauseNoResourceConfig:
buf.WriteString(fmt.Sprintf("\n # (because %s is not in configuration)", addr.Resource.Resource))
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
buf.WriteString(fmt.Sprintf("\n # (because %s was moved to %s, which is not in configuration)", change.PrevRunAddr, addr.Resource.Resource))
case plans.ResourceInstanceDeleteBecauseNoModule:
// FIXME: Ideally we'd truncate addr.Module to reflect the earliest
// step that doesn't exist, so it's clearer which call this refers
Expand Down
2 changes: 2 additions & 0 deletions internal/command/jsonplan/plan.go
Expand Up @@ -405,6 +405,8 @@ func (p *plan) marshalResourceChanges(resources []*plans.ResourceInstanceChangeS
r.ActionReason = "delete_because_each_key"
case plans.ResourceInstanceDeleteBecauseNoModule:
r.ActionReason = "delete_because_no_module"
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
r.ActionReason = "delete_because_no_move_target"
case plans.ResourceInstanceReadBecauseConfigUnknown:
r.ActionReason = "read_because_config_unknown"
case plans.ResourceInstanceReadBecauseDependencyPending:
Expand Down
3 changes: 3 additions & 0 deletions internal/command/views/json/change.go
Expand Up @@ -80,6 +80,7 @@ const (
ReasonDeleteBecauseCountIndex ChangeReason = "delete_because_count_index"
ReasonDeleteBecauseEachKey ChangeReason = "delete_because_each_key"
ReasonDeleteBecauseNoModule ChangeReason = "delete_because_no_module"
ReasonDeleteBecauseNoMoveTarget ChangeReason = "delete_because_no_move_target"
ReasonReadBecauseConfigUnknown ChangeReason = "read_because_config_unknown"
ReasonReadBecauseDependencyPending ChangeReason = "read_because_dependency_pending"
)
Expand Down Expand Up @@ -108,6 +109,8 @@ func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason
return ReasonDeleteBecauseNoModule
case plans.ResourceInstanceReadBecauseConfigUnknown:
return ReasonReadBecauseConfigUnknown
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
return ReasonDeleteBecauseNoMoveTarget
case plans.ResourceInstanceReadBecauseDependencyPending:
return ReasonReadBecauseDependencyPending
default:
Expand Down
6 changes: 6 additions & 0 deletions internal/plans/changes.go
Expand Up @@ -427,6 +427,12 @@ const (
// specific reasons for a particular instance to no longer be declared.
ResourceInstanceDeleteBecauseNoModule ResourceInstanceChangeActionReason = 'M'

// ResourceInstanceDeleteBecauseNoMoveTarget indicates that the resource
// address appears as the target ("to") in a moved block, but no
// configuration exists for that resource. According to our move rules,
// this combination evaluates to a deletion of the "new" resource.
ResourceInstanceDeleteBecauseNoMoveTarget ResourceInstanceChangeActionReason = 'A'

// ResourceInstanceReadBecauseConfigUnknown indicates that the resource
// must be read during apply (rather than during planning) because its
// configuration contains unknown values. This reason applies only to
Expand Down
51 changes: 27 additions & 24 deletions internal/plans/internal/planproto/planfile.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/plans/internal/planproto/planfile.proto
Expand Up @@ -149,6 +149,7 @@ enum ResourceInstanceActionReason {
REPLACE_BY_TRIGGERS = 9;
READ_BECAUSE_CONFIG_UNKNOWN = 10;
READ_BECAUSE_DEPENDENCY_PENDING = 11;
DELETE_BECAUSE_NO_MOVE_TARGET = 12;
}

message ResourceInstanceChange {
Expand Down
4 changes: 4 additions & 0 deletions internal/plans/planfile/tfplan.go
Expand Up @@ -282,6 +282,8 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla
ret.ActionReason = plans.ResourceInstanceReadBecauseConfigUnknown
case planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING:
ret.ActionReason = plans.ResourceInstanceReadBecauseDependencyPending
case planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET:
ret.ActionReason = plans.ResourceInstanceDeleteBecauseNoMoveTarget
default:
return nil, fmt.Errorf("resource has invalid action reason %s", rawChange.ActionReason)
}
Expand Down Expand Up @@ -633,6 +635,8 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_CONFIG_UNKNOWN
case plans.ResourceInstanceReadBecauseDependencyPending:
ret.ActionReason = planproto.ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING
case plans.ResourceInstanceDeleteBecauseNoMoveTarget:
ret.ActionReason = planproto.ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET
default:
return nil, fmt.Errorf("resource %s has unsupported action reason %s", change.Addr, change.ActionReason)
}
Expand Down
28 changes: 16 additions & 12 deletions internal/plans/resourceinstancechangeactionreason_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions internal/terraform/node_resource_plan_orphan.go
Expand Up @@ -157,6 +157,13 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
func (n *NodePlannableResourceInstanceOrphan) deleteActionReason(ctx EvalContext) plans.ResourceInstanceChangeActionReason {
cfg := n.Config
if cfg == nil {
if !n.Addr.Equal(n.prevRunAddr(ctx)) {
// This means the resource was moved - see also
// ResourceInstanceChange.Moved() which calculates
// this the same way.
return plans.ResourceInstanceDeleteBecauseNoMoveTarget
}

return plans.ResourceInstanceDeleteBecauseNoResourceConfig
}

Expand Down

0 comments on commit 891b5d5

Please sign in to comment.