terraform/internal/plans/action_invocation.go
Kristin Laemmert 70e074b8aa chore (actions): rename LifecycleActionTrigger -> ResourceActionTrigger in plan and proto
We'd previously removed all other references to "lifecycle" actions, which made this reference stand out. ResourceLifecycleActionTrigger is probably the most accurate name, but as this type just needs to be differentiated from InvokeActionTrigger I thought "resource" was enough (and I specifically wanted= to avoid lifecycle at all). I'm not super attached to the name, but I did think it would be clearer if we avoided Lifecycle as much as possible, since that's got some overlap with action subtypes.

In this instance, we call it a LifecycleActionTrigger because it's come from the resource's `lifecycle` block. This doesn't directly relate to the concept of LifecycleActions - even if we expand the design to have multiple action types (for example generic and lifecycle actions), both those actions types would use this same Trigger struct.
2026-02-23 15:17:45 -05:00

187 lines
5.3 KiB
Go

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package plans
import (
"fmt"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/lang/marks"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/tfdiags"
)
type ActionInvocationInstance struct {
Addr addrs.AbsActionInstance
ActionTrigger ActionTrigger
// Provider is the address of the provider configuration that was used
// to plan this action, and thus the configuration that must also be
// used to apply it.
ProviderAddr addrs.AbsProviderConfig
ConfigValue cty.Value
}
func (ai *ActionInvocationInstance) Equals(other *ActionInvocationInstance) bool {
// Since the trigger can be the same if it's a CLI invocation we also compare the action addr
return ai.Addr.Equal(other.Addr) && ai.ActionTrigger.Equals(other.ActionTrigger)
}
type ActionTrigger interface {
actionTriggerSigil()
TriggerEvent() configs.ActionTriggerEvent
String() string
Equals(to ActionTrigger) bool
Less(other ActionTrigger) bool
}
var (
_ ActionTrigger = (*ResourceActionTrigger)(nil)
_ ActionTrigger = (*InvokeActionTrigger)(nil)
)
// ResourceActionTrigger contains the action trigger configuration from the resource.
type ResourceActionTrigger struct {
TriggeringResourceAddr addrs.AbsResourceInstance
// Information about the trigger
// The event that triggered this action invocation.
ActionTriggerEvent configs.ActionTriggerEvent
// The index of the action_trigger block that triggered this invocation.
ActionTriggerBlockIndex int
// The index of the action in the events list of the action_trigger block
ActionsListIndex int
}
func (t *ResourceActionTrigger) TriggerEvent() configs.ActionTriggerEvent {
return t.ActionTriggerEvent
}
func (t *ResourceActionTrigger) actionTriggerSigil() {}
func (t *ResourceActionTrigger) String() string {
return t.TriggeringResourceAddr.String()
}
func (t *ResourceActionTrigger) Equals(other ActionTrigger) bool {
o, ok := other.(*ResourceActionTrigger)
if !ok {
return false
}
return t.TriggeringResourceAddr.Equal(o.TriggeringResourceAddr) &&
t.ActionTriggerBlockIndex == o.ActionTriggerBlockIndex &&
t.ActionsListIndex == o.ActionsListIndex &&
t.ActionTriggerEvent == o.ActionTriggerEvent
}
func (t *ResourceActionTrigger) Less(other ActionTrigger) bool {
o, ok := other.(*ResourceActionTrigger)
if !ok {
return false // We always want to show non-lifecycle actions first
}
return t.TriggeringResourceAddr.Less(o.TriggeringResourceAddr) ||
(t.TriggeringResourceAddr.Equal(o.TriggeringResourceAddr) &&
t.ActionTriggerBlockIndex < o.ActionTriggerBlockIndex) ||
(t.TriggeringResourceAddr.Equal(o.TriggeringResourceAddr) &&
t.ActionTriggerBlockIndex == o.ActionTriggerBlockIndex &&
t.ActionsListIndex < o.ActionsListIndex &&
t.ActionTriggerEvent < o.ActionTriggerEvent)
}
// InvokeActionTrigger contains the configuration for an action triggered
// (invoked) directly via CLI command.
type InvokeActionTrigger struct{}
func (t *InvokeActionTrigger) actionTriggerSigil() {}
func (t *InvokeActionTrigger) String() string {
return "CLI"
}
func (t *InvokeActionTrigger) TriggerEvent() configs.ActionTriggerEvent {
return configs.Invoke
}
func (t *InvokeActionTrigger) Equals(other ActionTrigger) bool {
_, ok := other.(*InvokeActionTrigger)
if !ok {
return false
}
return true // InvokeActionTriggers are always considered equal
}
func (t *InvokeActionTrigger) Less(other ActionTrigger) bool {
// always return true, actions that are equal are already ordered by
// address externally. these actions should go first anyway.
return true
}
// Encode produces a variant of the receiver that has its change values
// serialized so it can be written to a plan file. Pass the implied type of the
// corresponding resource type schema for correct operation.
func (ai *ActionInvocationInstance) Encode(schema *providers.ActionSchema) (*ActionInvocationInstanceSrc, error) {
ret := &ActionInvocationInstanceSrc{
Addr: ai.Addr,
ActionTrigger: ai.ActionTrigger,
ProviderAddr: ai.ProviderAddr,
}
if ai.ConfigValue != cty.NilVal {
ty := cty.DynamicPseudoType
if schema != nil {
ty = schema.ConfigSchema.ImpliedType()
}
unmarkedConfigValue, pvms := ai.ConfigValue.UnmarkDeepWithPaths()
sensitivePaths, otherMarks := marks.PathsWithMark(pvms, marks.Sensitive)
if len(otherMarks) > 0 {
return nil, fmt.Errorf("%s: error serializing action invocation with unexpected marks on config value: %#v. This is a bug in Terraform.", tfdiags.FormatCtyPath(otherMarks[0].Path), otherMarks[0].Marks)
}
var err error
ret.ConfigValue, err = NewDynamicValue(unmarkedConfigValue, ty)
ret.SensitiveConfigPaths = sensitivePaths
if err != nil {
return nil, err
}
}
return ret, nil
}
type ActionInvocationInstances []*ActionInvocationInstance
func (ais ActionInvocationInstances) DeepCopy() ActionInvocationInstances {
if ais == nil {
return ais
}
ret := make(ActionInvocationInstances, len(ais))
for i, ai := range ais {
ret[i] = ai.DeepCopy()
}
return ret
}
func (ai *ActionInvocationInstance) DeepCopy() *ActionInvocationInstance {
if ai == nil {
return ai
}
ret := *ai
return &ret
}