mirror of
https://github.com/hashicorp/terraform.git
synced 2026-06-09 08:58:34 -04:00
add SRO message for planned action invocation
This commit is contained in:
parent
e6dfb537e9
commit
90bdc053f2
5 changed files with 272 additions and 4 deletions
|
|
@ -59,6 +59,42 @@ func (c *ResourceInstanceChange) String() string {
|
|||
return fmt.Sprintf("%s: Plan to %s", c.Resource.Addr, c.Action)
|
||||
}
|
||||
|
||||
func NewPlannedActionInvocation(aiSrc *plans.ActionInvocationInstanceSrc) *ActionInvocation {
|
||||
ai := &ActionInvocation{
|
||||
Action: newActionAddr(aiSrc.Addr),
|
||||
}
|
||||
|
||||
if at, ok := aiSrc.ActionTrigger.(plans.LifecycleActionTrigger); ok {
|
||||
ai.LifecycleTrigger = &ActionInvocationLifecycleTrigger{
|
||||
TriggeringResource: newResourceAddr(at.TriggeringResourceAddr),
|
||||
TriggeringEvent: at.ActionTriggerEvent.String(),
|
||||
ActionTriggerBlockIndex: at.ActionTriggerBlockIndex,
|
||||
ActionsListIndex: at.ActionsListIndex,
|
||||
}
|
||||
}
|
||||
|
||||
return ai
|
||||
}
|
||||
|
||||
type ActionInvocation struct {
|
||||
Action ActionAddr `json:"action_addr"`
|
||||
LifecycleTrigger *ActionInvocationLifecycleTrigger `json:"lifecycle_trigger,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ActionInvocation) String() string {
|
||||
if c.LifecycleTrigger != nil {
|
||||
return fmt.Sprintf("%s: triggered by %s trigger on %s", c.Action.Addr, c.LifecycleTrigger.TriggeringEvent, c.LifecycleTrigger.TriggeringResource)
|
||||
}
|
||||
return c.Action.Addr
|
||||
}
|
||||
|
||||
type ActionInvocationLifecycleTrigger struct {
|
||||
TriggeringResource ResourceAddr `json:"triggering_resource"`
|
||||
TriggeringEvent string `json:"triggering_event"`
|
||||
ActionTriggerBlockIndex int `json:"action_trigger_block_index"`
|
||||
ActionsListIndex int `json:"actions_list_index"`
|
||||
}
|
||||
|
||||
type ChangeAction string
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ const (
|
|||
MessageDiagnostic MessageType = "diagnostic"
|
||||
|
||||
// Operation results
|
||||
MessageResourceDrift MessageType = "resource_drift"
|
||||
MessagePlannedChange MessageType = "planned_change"
|
||||
MessageChangeSummary MessageType = "change_summary"
|
||||
MessageOutputs MessageType = "outputs"
|
||||
MessageResourceDrift MessageType = "resource_drift"
|
||||
MessagePlannedChange MessageType = "planned_change"
|
||||
MessagePlannedActionInvocation MessageType = "planned_action_invocation"
|
||||
MessageChangeSummary MessageType = "change_summary"
|
||||
MessageOutputs MessageType = "outputs"
|
||||
|
||||
// Hook-driven messages
|
||||
MessageApplyStart MessageType = "apply_start"
|
||||
|
|
|
|||
|
|
@ -95,6 +95,14 @@ func (v *JSONView) PlannedChange(c *json.ResourceInstanceChange) {
|
|||
)
|
||||
}
|
||||
|
||||
func (v *JSONView) PlannedActionInvocation(action *json.ActionInvocation) {
|
||||
v.log.Info(
|
||||
fmt.Sprintf("planned action invocation: %s", action.Action.Action),
|
||||
"type", json.MessagePlannedActionInvocation,
|
||||
"invocation", action,
|
||||
)
|
||||
}
|
||||
|
||||
func (v *JSONView) ResourceDrift(c *json.ResourceInstanceChange) {
|
||||
v.log.Info(
|
||||
fmt.Sprintf("%s: Drift detected (%s)", c.Resource.Addr, c.Action),
|
||||
|
|
|
|||
|
|
@ -255,6 +255,9 @@ func (v *OperationJSON) Plan(plan *plans.Plan, schemas *terraform.Schemas) {
|
|||
}
|
||||
}
|
||||
cs.ActionInvocation = len(plan.Changes.ActionInvocations)
|
||||
for _, action := range plan.Changes.ActionInvocations {
|
||||
v.view.PlannedActionInvocation(json.NewPlannedActionInvocation(action))
|
||||
}
|
||||
|
||||
v.view.ChangeSummary(cs)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/command/arguments"
|
||||
"github.com/hashicorp/terraform/internal/configs"
|
||||
"github.com/hashicorp/terraform/internal/lang/globalref"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
|
|
@ -557,6 +558,225 @@ func TestOperationJSON_emergencyDumpState(t *testing.T) {
|
|||
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
||||
}
|
||||
|
||||
func TestOperationJSON_plan_with_actions(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := &OperationJSON{view: NewJSONView(NewView(streams))}
|
||||
|
||||
root := addrs.RootModuleInstance
|
||||
vpc, diags := addrs.ParseModuleInstanceStr("module.vpc")
|
||||
if len(diags) > 0 {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_resource", Name: "boop"}.Instance(addrs.NoKey).Absolute(root)
|
||||
beep := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_resource", Name: "beep"}.Instance(addrs.IntKey(0)).Absolute(vpc)
|
||||
|
||||
act1 := &plans.ActionInvocationInstanceSrc{
|
||||
Addr: addrs.Action{Type: "test_unlinked_action", Name: "hello"}.Instance(addrs.NoKey).Absolute(root),
|
||||
ActionTrigger: plans.LifecycleActionTrigger{
|
||||
TriggeringResourceAddr: boop,
|
||||
ActionTriggerEvent: configs.AfterCreate,
|
||||
ActionTriggerBlockIndex: 0,
|
||||
ActionsListIndex: 0,
|
||||
},
|
||||
}
|
||||
act2 := &plans.ActionInvocationInstanceSrc{
|
||||
Addr: addrs.Action{Type: "test_unlinked_other_action", Name: "world"}.Instance(addrs.NoKey).Absolute(root),
|
||||
ActionTrigger: plans.LifecycleActionTrigger{
|
||||
TriggeringResourceAddr: boop,
|
||||
ActionTriggerEvent: configs.AfterCreate,
|
||||
ActionTriggerBlockIndex: 0,
|
||||
ActionsListIndex: 1,
|
||||
},
|
||||
}
|
||||
act3 := &plans.ActionInvocationInstanceSrc{
|
||||
Addr: addrs.Action{Type: "test_unlinked_action", Name: "goodbye"}.Instance(addrs.IntKey(0)).Absolute(vpc),
|
||||
ActionTrigger: plans.LifecycleActionTrigger{
|
||||
TriggeringResourceAddr: beep,
|
||||
ActionTriggerEvent: configs.BeforeUpdate,
|
||||
ActionTriggerBlockIndex: 1,
|
||||
ActionsListIndex: 0,
|
||||
},
|
||||
}
|
||||
|
||||
plan := &plans.Plan{
|
||||
Changes: &plans.ChangesSrc{
|
||||
Resources: []*plans.ResourceInstanceChangeSrc{
|
||||
{
|
||||
Addr: boop,
|
||||
PrevRunAddr: boop,
|
||||
ChangeSrc: plans.ChangeSrc{Action: plans.Create},
|
||||
},
|
||||
{
|
||||
Addr: beep,
|
||||
PrevRunAddr: beep,
|
||||
ChangeSrc: plans.ChangeSrc{Action: plans.Update},
|
||||
},
|
||||
},
|
||||
|
||||
ActionInvocations: []*plans.ActionInvocationInstanceSrc{
|
||||
act1,
|
||||
act2,
|
||||
act3,
|
||||
},
|
||||
},
|
||||
}
|
||||
v.Plan(plan, testSchemas())
|
||||
|
||||
want := []map[string]interface{}{
|
||||
// Simple create
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "test_resource.boop: Plan to create",
|
||||
"@module": "terraform.ui",
|
||||
"type": "planned_change",
|
||||
"change": map[string]interface{}{
|
||||
"action": "create",
|
||||
"resource": map[string]interface{}{
|
||||
"addr": `test_resource.boop`,
|
||||
"implied_provider": "test",
|
||||
"module": "",
|
||||
"resource": `test_resource.boop`,
|
||||
"resource_key": nil,
|
||||
"resource_name": "boop",
|
||||
"resource_type": "test_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Simple update
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "module.vpc.test_resource.beep[0]: Plan to update",
|
||||
"@module": "terraform.ui",
|
||||
"type": "planned_change",
|
||||
"change": map[string]interface{}{
|
||||
"action": "update",
|
||||
"resource": map[string]interface{}{
|
||||
"addr": `module.vpc.test_resource.beep[0]`,
|
||||
"implied_provider": "test",
|
||||
"module": "module.vpc",
|
||||
"resource": `test_resource.beep[0]`,
|
||||
"resource_key": float64(0),
|
||||
"resource_name": "beep",
|
||||
"resource_type": "test_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Action invocation 1
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "planned action invocation: action.test_unlinked_action.hello",
|
||||
"@module": "terraform.ui",
|
||||
"type": "planned_action_invocation",
|
||||
"invocation": map[string]interface{}{
|
||||
"action_addr": map[string]interface{}{
|
||||
"addr": `action.test_unlinked_action.hello`,
|
||||
"implied_provider": "test",
|
||||
"module": "",
|
||||
"resource": `action.test_unlinked_action.hello`,
|
||||
"resource_key": nil,
|
||||
"resource_name": "hello",
|
||||
"resource_type": "test_unlinked_action",
|
||||
},
|
||||
"lifecycle_trigger": map[string]interface{}{
|
||||
"action_trigger_block_index": float64(0),
|
||||
"actions_list_index": float64(0),
|
||||
"triggering_event": "AfterCreate",
|
||||
"triggering_resource": map[string]interface{}{
|
||||
"addr": `test_resource.boop`,
|
||||
"implied_provider": "test",
|
||||
"module": "",
|
||||
"resource": `test_resource.boop`,
|
||||
"resource_key": nil,
|
||||
"resource_name": "boop",
|
||||
"resource_type": "test_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Action invocation 2
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "planned action invocation: action.test_unlinked_other_action.world",
|
||||
"@module": "terraform.ui",
|
||||
"type": "planned_action_invocation",
|
||||
"invocation": map[string]interface{}{
|
||||
"action_addr": map[string]interface{}{
|
||||
"addr": `action.test_unlinked_other_action.world`,
|
||||
"implied_provider": "test",
|
||||
"module": "",
|
||||
"resource": `action.test_unlinked_other_action.world`,
|
||||
"resource_key": nil,
|
||||
"resource_name": "world",
|
||||
"resource_type": "test_unlinked_other_action",
|
||||
},
|
||||
"lifecycle_trigger": map[string]interface{}{
|
||||
"action_trigger_block_index": float64(0),
|
||||
"actions_list_index": float64(1),
|
||||
"triggering_event": "AfterCreate",
|
||||
"triggering_resource": map[string]interface{}{
|
||||
"addr": `test_resource.boop`,
|
||||
"implied_provider": "test",
|
||||
"module": "",
|
||||
"resource": `test_resource.boop`,
|
||||
"resource_key": nil,
|
||||
"resource_name": "boop",
|
||||
"resource_type": "test_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Action invocation 3
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "planned action invocation: action.test_unlinked_action.goodbye[0]",
|
||||
"@module": "terraform.ui",
|
||||
"type": "planned_action_invocation",
|
||||
"invocation": map[string]interface{}{
|
||||
"action_addr": map[string]interface{}{
|
||||
"addr": `module.vpc.action.test_unlinked_action.goodbye[0]`,
|
||||
"implied_provider": "test",
|
||||
"module": "module.vpc",
|
||||
"resource": `action.test_unlinked_action.goodbye[0]`,
|
||||
"resource_key": float64(0),
|
||||
"resource_name": "goodbye",
|
||||
"resource_type": "test_unlinked_action",
|
||||
},
|
||||
"lifecycle_trigger": map[string]interface{}{
|
||||
"action_trigger_block_index": float64(1),
|
||||
"actions_list_index": float64(0),
|
||||
"triggering_event": "BeforeUpdate",
|
||||
"triggering_resource": map[string]interface{}{
|
||||
"addr": `module.vpc.test_resource.beep[0]`,
|
||||
"implied_provider": "test",
|
||||
"module": "module.vpc",
|
||||
"resource": `test_resource.beep[0]`,
|
||||
"resource_key": float64(0),
|
||||
"resource_name": "beep",
|
||||
"resource_type": "test_resource",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Change summary with action invocations
|
||||
{
|
||||
"@level": "info",
|
||||
"@message": "Plan: 1 to add, 1 to change, 0 to destroy. Actions: 3 to invoke.",
|
||||
"@module": "terraform.ui",
|
||||
"type": "change_summary",
|
||||
"changes": map[string]interface{}{
|
||||
"operation": "plan",
|
||||
"action_invocation": float64(3),
|
||||
"add": float64(1),
|
||||
"import": float64(0),
|
||||
"change": float64(1),
|
||||
"remove": float64(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testJSONViewOutputEquals(t, done(t).Stdout(), want)
|
||||
}
|
||||
|
||||
func TestOperationJSON_planNoChanges(t *testing.T) {
|
||||
streams, done := terminal.StreamsForTesting(t)
|
||||
v := &OperationJSON{view: NewJSONView(NewView(streams))}
|
||||
|
|
|
|||
Loading…
Reference in a new issue