Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions internal/command/jsonformat/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,18 @@ func precomputeDiffs(plan Plan, mode plans.Mode) diffs {
after := []jsonplan.ActionInvocation{}

for _, action := range plan.ActionInvocations {
if action.TriggeringResourceAddress != change.Address {
if action.LifecycleActionTrigger == nil || action.LifecycleActionTrigger.TriggeringResourceAddress != change.Address {
continue
}

switch action.TriggerEvent {
switch action.LifecycleActionTrigger.ActionTriggerEvent {
case configs.BeforeCreate.String(), configs.BeforeUpdate.String(), configs.BeforeDestroy.String():
before = append(before, action)
case configs.AfterCreate.String(), configs.AfterUpdate.String(), configs.AfterDestroy.String():
after = append(after, action)
default:
// The switch should be exhaustive.
panic(fmt.Sprintf("Unexpected triggering event when rendering action %s", action.TriggerEvent))
panic(fmt.Sprintf("Unexpected triggering event when rendering action %s", action.LifecycleActionTrigger.ActionTriggerEvent))
}
}

Expand Down
148 changes: 80 additions & 68 deletions internal/command/jsonformat/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8289,25 +8289,23 @@ func TestResourceChange_actions(t *testing.T) {
},
}

ptr := func(i int) *int {
return &i
}

for name, tc := range map[string]struct {
actionInvocations []jsonplan.ActionInvocation
output string
}{
"before actions": {
actionInvocations: []jsonplan.ActionInvocation{
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(0),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "BeforeCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 0,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "BeforeCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"),
"disk": cty.ObjectVal(map[string]cty.Value{
Expand Down Expand Up @@ -8342,14 +8340,16 @@ func TestResourceChange_actions(t *testing.T) {
"after actions": {
actionInvocations: []jsonplan.ActionInvocation{
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(0),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "AfterCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 0,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "AfterCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("1D5F5E9E-F2E5-401B-9ED5-692A215AC67E"),
"disk": cty.ObjectVal(map[string]cty.Value{
Expand Down Expand Up @@ -8384,66 +8384,76 @@ func TestResourceChange_actions(t *testing.T) {
"before and after actions": {
actionInvocations: []jsonplan.ActionInvocation{
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(0),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "BeforeCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 0,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "BeforeCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("first-block-and-action"),
})),
},
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(0),
ActionsListIndex: ptr(1),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "BeforeCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 0,
ActionsListIndex: 1,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "BeforeCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("first-block-second-action"),
})),
},
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(1),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "AfterCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 1,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "AfterCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("second-block-first-action"),
})),
},
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(2),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "AfterCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 2,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "AfterCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("third-block-first-action"),
})),
},
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(3),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "BeforeCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 3,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "BeforeCreate",
},
ConfigValues: marshalConfigValues(cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("fourth-block-first-action"),
})),
Expand Down Expand Up @@ -8488,14 +8498,16 @@ func TestResourceChange_actions(t *testing.T) {
"no config value": {
actionInvocations: []jsonplan.ActionInvocation{
{
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
ActionTriggerBlockIndex: ptr(0),
ActionsListIndex: ptr(0),
TriggeringResourceAddress: triggeringResourceAddr.String(),
TriggerEvent: "BeforeCreate",
Address: "action.test_unlinked.hello",
Type: "test_unlinked",
Name: "hello",
ProviderName: "registry.terraform.io/hashicorp/test",
LifecycleActionTrigger: &jsonplan.LifecycleActionTrigger{
ActionTriggerBlockIndex: 0,
ActionsListIndex: 0,
TriggeringResourceAddress: triggeringResourceAddr.String(),
ActionTriggerEvent: "BeforeCreate",
},
},
},
output: ` # test_instance.example will be created
Expand Down
64 changes: 36 additions & 28 deletions internal/command/jsonplan/action_invocations.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,39 +31,42 @@ type ActionInvocation struct {
// offering "google_compute_instance".
ProviderName string `json:"provider_name,omitempty"`

// These fields below are used for actions invoked during plan / apply, they are not applicable
// for terraform invoke.

// ActionTriggerBlockIndex is the index of the action trigger block
ActionTriggerBlockIndex *int `json:"action_trigger_block_index,omitempty"`
// ActionsListIndex is the index of the action in the actions list
ActionsListIndex *int `json:"actions_list_index,omitempty"`
// TriggeringResourceAddress is the address of the resource that triggered the action
LifecycleActionTrigger *LifecycleActionTrigger `json:"lifecycle_action_trigger,omitempty"`
InvokeCmdActionTrigger *InvokeCmdActionTrigger `json:"invoke_cmd_action_trigger,omitempty"`
}

type LifecycleActionTrigger struct {
TriggeringResourceAddress string `json:"triggering_resource_address,omitempty"`
// TriggerEvent is the event that triggered the action
TriggerEvent string `json:"trigger_event,omitempty"`
ActionTriggerEvent string `json:"action_trigger_event,omitempty"`
ActionTriggerBlockIndex int `json:"action_trigger_block_index,omitempty"`
ActionsListIndex int `json:"actions_list_index,omitempty"`
}

type InvokeCmdActionTrigger struct {
ActionTriggerEvent string `json:"action_trigger_event,omitempty"`
}

func ActionInvocationCompare(a, b ActionInvocation) int {
if a.TriggeringResourceAddress < b.TriggeringResourceAddress {
return -1
} else if a.TriggeringResourceAddress > b.TriggeringResourceAddress {
return 1
}
if a.LifecycleActionTrigger != nil && b.LifecycleActionTrigger != nil {
latA := *a.LifecycleActionTrigger
latB := *b.LifecycleActionTrigger

if a.ActionTriggerBlockIndex != nil && b.ActionTriggerBlockIndex != nil {
if *a.ActionTriggerBlockIndex < *b.ActionTriggerBlockIndex {
if latA.TriggeringResourceAddress < latB.TriggeringResourceAddress {
return -1
} else if *a.ActionTriggerBlockIndex > *b.ActionTriggerBlockIndex {
} else if latA.TriggeringResourceAddress > latB.TriggeringResourceAddress {
return 1
}
}

if a.ActionsListIndex != nil && b.ActionsListIndex != nil {
if *a.ActionsListIndex < *b.ActionsListIndex {
if latA.ActionTriggerBlockIndex < latB.ActionTriggerBlockIndex {
return -1
} else if latA.ActionTriggerBlockIndex > latB.ActionTriggerBlockIndex {
return 1
}

if latA.ActionsListIndex < latB.ActionsListIndex {
return -1

} else if *a.ActionsListIndex > *b.ActionsListIndex {
} else if latA.ActionsListIndex > latB.ActionsListIndex {
return 1
}
}
Expand Down Expand Up @@ -111,13 +114,18 @@ func MarshalActionInvocations(actions []*plans.ActionInvocationInstanceSrc, sche
Type: action.Addr.Action.Action.Type,
Name: action.Addr.Action.Action.Name,
ProviderName: action.ProviderAddr.Provider.String(),
}

// These fields are only used for non-CLI actions. We will need to find another format
// once we support terraform invoke.
ActionTriggerBlockIndex: &action.ActionTriggerBlockIndex,
ActionsListIndex: &action.ActionsListIndex,
TriggeringResourceAddress: action.TriggeringResourceAddr.String(),
TriggerEvent: action.TriggerEvent.String(),
switch at := action.ActionTrigger.(type) {
case plans.LifecycleActionTrigger:
ai.LifecycleActionTrigger = &LifecycleActionTrigger{
TriggeringResourceAddress: at.TriggeringResourceAddr.String(),
ActionTriggerEvent: at.TriggerEvent().String(),
ActionTriggerBlockIndex: at.ActionTriggerBlockIndex,
ActionsListIndex: at.ActionsListIndex,
}
default:
return ret, fmt.Errorf("unsupported action trigger type: %T", at)
}

if actionDec.ConfigValue != cty.NilVal {
Expand Down
12 changes: 8 additions & 4 deletions internal/command/views/hook_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/zclconf/go-cty/cty"

"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/terminal"
"github.com/hashicorp/terraform/internal/terraform"
Expand All @@ -32,10 +33,13 @@ func testJSONHookResourceID(addr addrs.AbsResourceInstance) terraform.HookResour

func testJSONHookActionID(actionAddr addrs.AbsActionInstance, triggeringResourceAddr addrs.AbsResourceInstance, actionTriggerIndex int, actionsListIndex int) terraform.HookActionIdentity {
return terraform.HookActionIdentity{
Addr: actionAddr,
TriggeringResourceAddr: triggeringResourceAddr,
ActionTriggerBlockIndex: actionTriggerIndex,
ActionsListIndex: actionsListIndex,
Addr: actionAddr,
ActionTrigger: plans.LifecycleActionTrigger{
TriggeringResourceAddr: triggeringResourceAddr,
ActionTriggerBlockIndex: actionTriggerIndex,
ActionsListIndex: actionsListIndex,
ActionTriggerEvent: configs.AfterCreate,
},
}
}

Expand Down
Loading