plans: indicate when resource deleted due to move (#31695)

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 that resource B will be
destroyed because it does not exist in configuration, without explicitly
connecting this to 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.
This commit is contained in:
kmoe 2022-08-30 18:01:29 +01:00 committed by GitHub
parent 1347aa29fd
commit dec48a8510
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 53 additions and 18 deletions

View File

@ -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

View File

@ -413,6 +413,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:

View File

@ -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"
)
@ -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:

View File

@ -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

View File

@ -152,6 +152,7 @@ const (
ResourceInstanceActionReason_REPLACE_BY_TRIGGERS ResourceInstanceActionReason = 9
ResourceInstanceActionReason_READ_BECAUSE_CONFIG_UNKNOWN ResourceInstanceActionReason = 10
ResourceInstanceActionReason_READ_BECAUSE_DEPENDENCY_PENDING ResourceInstanceActionReason = 11
ResourceInstanceActionReason_DELETE_BECAUSE_NO_MOVE_TARGET ResourceInstanceActionReason = 12
)
// Enum value maps for ResourceInstanceActionReason.
@ -169,6 +170,7 @@ var (
9: "REPLACE_BY_TRIGGERS",
10: "READ_BECAUSE_CONFIG_UNKNOWN",
11: "READ_BECAUSE_DEPENDENCY_PENDING",
12: "DELETE_BECAUSE_NO_MOVE_TARGET",
}
ResourceInstanceActionReason_value = map[string]int32{
"NONE": 0,
@ -183,6 +185,7 @@ var (
"REPLACE_BY_TRIGGERS": 9,
"READ_BECAUSE_CONFIG_UNKNOWN": 10,
"READ_BECAUSE_DEPENDENCY_PENDING": 11,
"DELETE_BECAUSE_NO_MOVE_TARGET": 12,
}
)
@ -1391,7 +1394,7 @@ var file_planfile_proto_rawDesc = []byte{
0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44,
0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54,
0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48,
0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x2a, 0x86, 0x03, 0x0a, 0x1c,
0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x2a, 0xa9, 0x03, 0x0a, 0x1c,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04,
0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43,
@ -1416,11 +1419,14 @@ var file_planfile_proto_rawDesc = []byte{
0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x0a, 0x12,
0x23, 0x0a, 0x1f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f,
0x44, 0x45, 0x50, 0x45, 0x4e, 0x44, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49,
0x4e, 0x47, 0x10, 0x0b, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72,
0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f,
0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70,
0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x4e, 0x47, 0x10, 0x0b, 0x12, 0x21, 0x0a, 0x1d, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x42,
0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x54,
0x41, 0x52, 0x47, 0x45, 0x54, 0x10, 0x0c, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (

View File

@ -151,6 +151,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 {

View File

@ -331,6 +331,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)
}
@ -705,6 +707,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)
}

View File

@ -18,6 +18,7 @@ func _() {
_ = x[ResourceInstanceDeleteBecauseCountIndex-67]
_ = x[ResourceInstanceDeleteBecauseEachKey-69]
_ = x[ResourceInstanceDeleteBecauseNoModule-77]
_ = x[ResourceInstanceDeleteBecauseNoMoveTarget-65]
_ = x[ResourceInstanceReadBecauseConfigUnknown-63]
_ = x[ResourceInstanceReadBecauseDependencyPending-33]
}
@ -26,16 +27,17 @@ const (
_ResourceInstanceChangeActionReason_name_0 = "ResourceInstanceChangeNoReason"
_ResourceInstanceChangeActionReason_name_1 = "ResourceInstanceReadBecauseDependencyPending"
_ResourceInstanceChangeActionReason_name_2 = "ResourceInstanceReadBecauseConfigUnknown"
_ResourceInstanceChangeActionReason_name_3 = "ResourceInstanceDeleteBecauseCountIndexResourceInstanceReplaceByTriggersResourceInstanceDeleteBecauseEachKeyResourceInstanceReplaceBecauseCannotUpdate"
_ResourceInstanceChangeActionReason_name_4 = "ResourceInstanceDeleteBecauseNoModuleResourceInstanceDeleteBecauseNoResourceConfig"
_ResourceInstanceChangeActionReason_name_5 = "ResourceInstanceReplaceByRequest"
_ResourceInstanceChangeActionReason_name_6 = "ResourceInstanceReplaceBecauseTainted"
_ResourceInstanceChangeActionReason_name_7 = "ResourceInstanceDeleteBecauseWrongRepetition"
_ResourceInstanceChangeActionReason_name_3 = "ResourceInstanceDeleteBecauseNoMoveTarget"
_ResourceInstanceChangeActionReason_name_4 = "ResourceInstanceDeleteBecauseCountIndexResourceInstanceReplaceByTriggersResourceInstanceDeleteBecauseEachKeyResourceInstanceReplaceBecauseCannotUpdate"
_ResourceInstanceChangeActionReason_name_5 = "ResourceInstanceDeleteBecauseNoModuleResourceInstanceDeleteBecauseNoResourceConfig"
_ResourceInstanceChangeActionReason_name_6 = "ResourceInstanceReplaceByRequest"
_ResourceInstanceChangeActionReason_name_7 = "ResourceInstanceReplaceBecauseTainted"
_ResourceInstanceChangeActionReason_name_8 = "ResourceInstanceDeleteBecauseWrongRepetition"
)
var (
_ResourceInstanceChangeActionReason_index_3 = [...]uint8{0, 39, 72, 108, 150}
_ResourceInstanceChangeActionReason_index_4 = [...]uint8{0, 37, 82}
_ResourceInstanceChangeActionReason_index_4 = [...]uint8{0, 39, 72, 108, 150}
_ResourceInstanceChangeActionReason_index_5 = [...]uint8{0, 37, 82}
)
func (i ResourceInstanceChangeActionReason) String() string {
@ -46,18 +48,20 @@ func (i ResourceInstanceChangeActionReason) String() string {
return _ResourceInstanceChangeActionReason_name_1
case i == 63:
return _ResourceInstanceChangeActionReason_name_2
case i == 65:
return _ResourceInstanceChangeActionReason_name_3
case 67 <= i && i <= 70:
i -= 67
return _ResourceInstanceChangeActionReason_name_3[_ResourceInstanceChangeActionReason_index_3[i]:_ResourceInstanceChangeActionReason_index_3[i+1]]
return _ResourceInstanceChangeActionReason_name_4[_ResourceInstanceChangeActionReason_index_4[i]:_ResourceInstanceChangeActionReason_index_4[i+1]]
case 77 <= i && i <= 78:
i -= 77
return _ResourceInstanceChangeActionReason_name_4[_ResourceInstanceChangeActionReason_index_4[i]:_ResourceInstanceChangeActionReason_index_4[i+1]]
return _ResourceInstanceChangeActionReason_name_5[_ResourceInstanceChangeActionReason_index_5[i]:_ResourceInstanceChangeActionReason_index_5[i+1]]
case i == 82:
return _ResourceInstanceChangeActionReason_name_5
case i == 84:
return _ResourceInstanceChangeActionReason_name_6
case i == 87:
case i == 84:
return _ResourceInstanceChangeActionReason_name_7
case i == 87:
return _ResourceInstanceChangeActionReason_name_8
default:
return "ResourceInstanceChangeActionReason(" + strconv.FormatInt(int64(i), 10) + ")"
}

View File

@ -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
}