command/views: Add reason to JSON planned change

Now that we have extra information about the reason for a given resource
action, include that in the JSON log output for planned changes.
This commit is contained in:
Alisdair McDiarmid 2021-05-03 06:49:42 -04:00
parent 6bed3008a5
commit 7b23fa7877
2 changed files with 60 additions and 4 deletions

View File

@ -10,6 +10,7 @@ func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *Resourc
c := &ResourceInstanceChange{ c := &ResourceInstanceChange{
Resource: newResourceAddr(change.Addr), Resource: newResourceAddr(change.Addr),
Action: changeAction(change.Action), Action: changeAction(change.Action),
Reason: changeReason(change.ActionReason),
} }
return c return c
@ -18,6 +19,7 @@ func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *Resourc
type ResourceInstanceChange struct { type ResourceInstanceChange struct {
Resource ResourceAddr `json:"resource"` Resource ResourceAddr `json:"resource"`
Action ChangeAction `json:"action"` Action ChangeAction `json:"action"`
Reason ChangeReason `json:"reason,omitempty"`
} }
func (c *ResourceInstanceChange) String() string { func (c *ResourceInstanceChange) String() string {
@ -53,3 +55,31 @@ func changeAction(action plans.Action) ChangeAction {
return ActionNoOp return ActionNoOp
} }
} }
type ChangeReason string
const (
ReasonNone ChangeReason = ""
ReasonTainted ChangeReason = "tainted"
ReasonRequested ChangeReason = "requested"
ReasonCannotUpdate ChangeReason = "cannot_update"
ReasonUnknown ChangeReason = "unknown"
)
func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason {
switch reason {
case plans.ResourceInstanceChangeNoReason:
return ReasonNone
case plans.ResourceInstanceReplaceBecauseTainted:
return ReasonTainted
case plans.ResourceInstanceReplaceByRequest:
return ReasonRequested
case plans.ResourceInstanceReplaceBecauseCannotUpdate:
return ReasonCannotUpdate
default:
// This should never happen, but there's no good way to guarantee
// exhaustive handling of the enum, so a generic fall back is better
// than a misleading result or a panic
return ReasonUnknown
}
}

View File

@ -433,9 +433,16 @@ func TestOperationJSON_plannedChange(t *testing.T) {
boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "boop"} boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "boop"}
derp := addrs.Resource{Mode: addrs.DataResourceMode, Type: "test_source", Name: "derp"} derp := addrs.Resource{Mode: addrs.DataResourceMode, Type: "test_source", Name: "derp"}
// Replace requested by user
v.PlannedChange(&plans.ResourceInstanceChangeSrc{
Addr: boop.Instance(addrs.IntKey(0)).Absolute(root),
ChangeSrc: plans.ChangeSrc{Action: plans.DeleteThenCreate},
ActionReason: plans.ResourceInstanceReplaceByRequest,
})
// Simple create // Simple create
v.PlannedChange(&plans.ResourceInstanceChangeSrc{ v.PlannedChange(&plans.ResourceInstanceChangeSrc{
Addr: boop.Instance(addrs.IntKey(0)).Absolute(root), Addr: boop.Instance(addrs.IntKey(1)).Absolute(root),
ChangeSrc: plans.ChangeSrc{Action: plans.Create}, ChangeSrc: plans.ChangeSrc{Action: plans.Create},
}) })
@ -445,15 +452,16 @@ func TestOperationJSON_plannedChange(t *testing.T) {
ChangeSrc: plans.ChangeSrc{Action: plans.Delete}, ChangeSrc: plans.ChangeSrc{Action: plans.Delete},
}) })
// Expect one message only, as the data source deletion should be a no-op // Expect only two messages, as the data source deletion should be a no-op
want := []map[string]interface{}{ want := []map[string]interface{}{
{ {
"@level": "info", "@level": "info",
"@message": "test_instance.boop[0]: Plan to create", "@message": "test_instance.boop[0]: Plan to replace",
"@module": "terraform.ui", "@module": "terraform.ui",
"type": "planned_change", "type": "planned_change",
"change": map[string]interface{}{ "change": map[string]interface{}{
"action": "create", "action": "replace",
"reason": "requested",
"resource": map[string]interface{}{ "resource": map[string]interface{}{
"addr": `test_instance.boop[0]`, "addr": `test_instance.boop[0]`,
"implied_provider": "test", "implied_provider": "test",
@ -465,6 +473,24 @@ func TestOperationJSON_plannedChange(t *testing.T) {
}, },
}, },
}, },
{
"@level": "info",
"@message": "test_instance.boop[1]: Plan to create",
"@module": "terraform.ui",
"type": "planned_change",
"change": map[string]interface{}{
"action": "create",
"resource": map[string]interface{}{
"addr": `test_instance.boop[1]`,
"implied_provider": "test",
"module": "",
"resource": `test_instance.boop[1]`,
"resource_key": float64(1),
"resource_name": "boop",
"resource_type": "test_instance",
},
},
},
} }
testJSONViewOutputEquals(t, done(t).Stdout(), want) testJSONViewOutputEquals(t, done(t).Stdout(), want)