mirror of https://github.com/hashicorp/terraform
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
20 KiB
645 lines
20 KiB
// Copyright (c) HashiCorp, Inc.
|
|
// 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/genconfig"
|
|
"github.com/hashicorp/terraform/internal/lang/marks"
|
|
"github.com/hashicorp/terraform/internal/providers"
|
|
"github.com/hashicorp/terraform/internal/schemarepo"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
)
|
|
|
|
// ChangesSrc describes various actions that Terraform will attempt to take if
|
|
// the corresponding plan is applied.
|
|
//
|
|
// A Changes object can be rendered into a visual diff (by the caller, using
|
|
// code in another package) for display to the user.
|
|
type ChangesSrc struct {
|
|
// Resources tracks planned changes to resource instance objects.
|
|
Resources []*ResourceInstanceChangeSrc
|
|
|
|
Queries []*QueryInstanceSrc
|
|
|
|
// ActionInvocations tracks planned action invocations, which may have
|
|
// embedded resource instance changes.
|
|
ActionInvocations []*ActionInvocationInstanceSrc
|
|
|
|
// Outputs tracks planned changes output values.
|
|
//
|
|
// Note that although an in-memory plan contains planned changes for
|
|
// outputs throughout the configuration, a plan serialized
|
|
// to disk retains only the root outputs because they are
|
|
// externally-visible, while other outputs are implementation details and
|
|
// can be easily re-calculated during the apply phase. Therefore only root
|
|
// module outputs will survive a round-trip through a plan file.
|
|
Outputs []*OutputChangeSrc
|
|
}
|
|
|
|
func NewChangesSrc() *ChangesSrc {
|
|
return &ChangesSrc{}
|
|
}
|
|
|
|
func (c *ChangesSrc) Empty() bool {
|
|
for _, res := range c.Resources {
|
|
if res.Action != NoOp || res.Moved() {
|
|
return false
|
|
}
|
|
|
|
if res.Importing != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for _, out := range c.Outputs {
|
|
if out.Addr.Module.IsRoot() && out.Action != NoOp {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if len(c.ActionInvocations) > 0 {
|
|
// action invocations can be applied
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ResourceInstance returns the planned change for the current object of the
|
|
// resource instance of the given address, if any. Returns nil if no change is
|
|
// planned.
|
|
func (c *ChangesSrc) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc {
|
|
for _, rc := range c.Resources {
|
|
if rc.Addr.Equal(addr) && rc.DeposedKey == states.NotDeposed {
|
|
return rc
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ResourceInstanceDeposed returns the plan change of a deposed object of
|
|
// the resource instance of the given address, if any. Returns nil if no change
|
|
// is planned.
|
|
func (c *ChangesSrc) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc {
|
|
for _, rc := range c.Resources {
|
|
if rc.Addr.Equal(addr) && rc.DeposedKey == key {
|
|
return rc
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// OutputValue returns the planned change for the output value with the
|
|
//
|
|
// given address, if any. Returns nil if no change is planned.
|
|
func (c *ChangesSrc) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc {
|
|
for _, oc := range c.Outputs {
|
|
if oc.Addr.Equal(addr) {
|
|
return oc
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Decode decodes all the stored resource and output changes into a new *Changes value.
|
|
func (c *ChangesSrc) Decode(schemas *schemarepo.Schemas) (*Changes, error) {
|
|
changes := NewChanges()
|
|
|
|
for _, rcs := range c.Resources {
|
|
p, ok := schemas.Providers[rcs.ProviderAddr.Provider]
|
|
if !ok {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing provider %s for %s", rcs.ProviderAddr, rcs.Addr)
|
|
}
|
|
|
|
var schema providers.Schema
|
|
switch rcs.Addr.Resource.Resource.Mode {
|
|
case addrs.ManagedResourceMode:
|
|
schema = p.ResourceTypes[rcs.Addr.Resource.Resource.Type]
|
|
case addrs.DataResourceMode:
|
|
schema = p.DataSources[rcs.Addr.Resource.Resource.Type]
|
|
default:
|
|
panic(fmt.Sprintf("unexpected resource mode %s", rcs.Addr.Resource.Resource.Mode))
|
|
}
|
|
|
|
if schema.Body == nil {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing schema for %s", rcs.Addr)
|
|
}
|
|
|
|
rc, err := rcs.Decode(schema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rc.Before = marks.MarkPaths(rc.Before, marks.Sensitive, rcs.BeforeSensitivePaths)
|
|
rc.After = marks.MarkPaths(rc.After, marks.Sensitive, rcs.AfterSensitivePaths)
|
|
|
|
changes.Resources = append(changes.Resources, rc)
|
|
}
|
|
|
|
for _, qis := range c.Queries {
|
|
p, ok := schemas.Providers[qis.ProviderAddr.Provider]
|
|
if !ok {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing provider %s for %s", qis.ProviderAddr, qis.Addr)
|
|
}
|
|
schema := p.ListResourceTypes[qis.Addr.Resource.Resource.Type]
|
|
|
|
if schema.Body == nil {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing schema for %s", qis.Addr)
|
|
}
|
|
|
|
query, err := qis.Decode(schema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
changes.Queries = append(changes.Queries, query)
|
|
}
|
|
|
|
for _, ais := range c.ActionInvocations {
|
|
p, ok := schemas.Providers[ais.ProviderAddr.Provider]
|
|
if !ok {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing provider %s for %s", ais.ProviderAddr, ais.Addr)
|
|
}
|
|
schema, ok := p.Actions[ais.Addr.Action.Action.Type]
|
|
if !ok {
|
|
return nil, fmt.Errorf("ChangesSrc.Decode: missing schema for %s", ais.Addr.Action.Action.Type)
|
|
}
|
|
|
|
action, err := ais.Decode(&schema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
changes.ActionInvocations = append(changes.ActionInvocations, action)
|
|
}
|
|
|
|
for _, ocs := range c.Outputs {
|
|
oc, err := ocs.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changes.Outputs = append(changes.Outputs, oc)
|
|
}
|
|
return changes, nil
|
|
}
|
|
|
|
// AppendResourceInstanceChange records the given resource instance change in
|
|
// the set of planned resource changes.
|
|
func (c *ChangesSrc) AppendResourceInstanceChange(change *ResourceInstanceChangeSrc) {
|
|
if c == nil {
|
|
panic("AppendResourceInstanceChange on nil ChangesSync")
|
|
}
|
|
|
|
s := change.DeepCopy()
|
|
c.Resources = append(c.Resources, s)
|
|
}
|
|
|
|
type QueryInstanceSrc struct {
|
|
Addr addrs.AbsResourceInstance
|
|
ProviderAddr addrs.AbsProviderConfig
|
|
Results DynamicValue
|
|
Generated genconfig.ImportGroup
|
|
}
|
|
|
|
func (qis *QueryInstanceSrc) Decode(schema providers.Schema) (*QueryInstance, error) {
|
|
query, err := qis.Results.Decode(schema.Body.ImpliedType())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &QueryInstance{
|
|
Addr: qis.Addr,
|
|
Results: QueryResults{
|
|
Value: query,
|
|
Generated: qis.Generated,
|
|
},
|
|
ProviderAddr: qis.ProviderAddr,
|
|
}, nil
|
|
}
|
|
|
|
// ResourceInstanceChangeSrc is a not-yet-decoded ResourceInstanceChange.
|
|
// Pass the associated resource type's schema type to method Decode to
|
|
// obtain a ResourceInstanceChange.
|
|
type ResourceInstanceChangeSrc struct {
|
|
// Addr is the absolute address of the resource instance that the change
|
|
// will apply to.
|
|
//
|
|
// THIS IS NOT A SUFFICIENT UNIQUE IDENTIFIER! It doesn't consider the
|
|
// fact that multiple objects for the same resource instance might be
|
|
// present in the same plan; use the ObjectAddr method instead if you
|
|
// need a unique address for a particular change.
|
|
Addr addrs.AbsResourceInstance
|
|
|
|
// DeposedKey is the identifier for a deposed object associated with the
|
|
// given instance, or states.NotDeposed if this change applies to the
|
|
// current object.
|
|
//
|
|
// A Replace change for a resource with create_before_destroy set will
|
|
// create a new DeposedKey temporarily during replacement. In that case,
|
|
// DeposedKey in the plan is always states.NotDeposed, representing that
|
|
// the current object is being replaced with the deposed.
|
|
DeposedKey states.DeposedKey
|
|
|
|
// PrevRunAddr is the absolute address that this resource instance had at
|
|
// the conclusion of a previous run.
|
|
//
|
|
// This will typically be the same as Addr, but can be different if the
|
|
// previous resource instance was subject to a "moved" block that we
|
|
// handled in the process of creating this plan.
|
|
//
|
|
// For the initial creation of a resource instance there isn't really any
|
|
// meaningful "previous run address", but PrevRunAddr will still be set
|
|
// equal to Addr in that case in order to simplify logic elsewhere which
|
|
// aims to detect and react to the movement of instances between addresses.
|
|
PrevRunAddr addrs.AbsResourceInstance
|
|
|
|
// Provider is the address of the provider configuration that was used
|
|
// to plan this change, and thus the configuration that must also be
|
|
// used to apply it.
|
|
ProviderAddr addrs.AbsProviderConfig
|
|
|
|
// ChangeSrc is an embedded description of the not-yet-decoded change.
|
|
ChangeSrc
|
|
|
|
// ActionReason is an optional extra indication of why we chose the
|
|
// action recorded in Change.Action for this particular resource instance.
|
|
//
|
|
// This is an approximate mechanism only for the purpose of explaining the
|
|
// plan to end-users in the UI and is not to be used for any
|
|
// decision-making during the apply step; if apply behavior needs to vary
|
|
// depending on the "action reason" then the information for that decision
|
|
// must be recorded more precisely elsewhere for that purpose.
|
|
//
|
|
// See the field of the same name in ResourceInstanceChange for more
|
|
// details.
|
|
ActionReason ResourceInstanceChangeActionReason
|
|
|
|
// RequiredReplace is a set of paths that caused the change action to be
|
|
// Replace rather than Update. Always nil if the change action is not
|
|
// Replace.
|
|
RequiredReplace cty.PathSet
|
|
|
|
// Private allows a provider to stash any extra data that is opaque to
|
|
// Terraform that relates to this change. Terraform will save this
|
|
// byte-for-byte and return it to the provider in the apply call.
|
|
Private []byte
|
|
}
|
|
|
|
func (rcs *ResourceInstanceChangeSrc) ObjectAddr() addrs.AbsResourceInstanceObject {
|
|
return addrs.AbsResourceInstanceObject{
|
|
ResourceInstance: rcs.Addr,
|
|
DeposedKey: rcs.DeposedKey,
|
|
}
|
|
}
|
|
|
|
// Decode unmarshals the raw representation of the instance object being
|
|
// changed. Pass the implied type of the corresponding resource type schema
|
|
// for correct operation.
|
|
func (rcs *ResourceInstanceChangeSrc) Decode(schema providers.Schema) (*ResourceInstanceChange, error) {
|
|
change, err := rcs.ChangeSrc.Decode(&schema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
prevRunAddr := rcs.PrevRunAddr
|
|
if prevRunAddr.Resource.Resource.Type == "" {
|
|
// Suggests an old caller that hasn't been properly updated to
|
|
// populate this yet.
|
|
prevRunAddr = rcs.Addr
|
|
}
|
|
return &ResourceInstanceChange{
|
|
Addr: rcs.Addr,
|
|
PrevRunAddr: prevRunAddr,
|
|
DeposedKey: rcs.DeposedKey,
|
|
ProviderAddr: rcs.ProviderAddr,
|
|
Change: *change,
|
|
ActionReason: rcs.ActionReason,
|
|
RequiredReplace: rcs.RequiredReplace,
|
|
Private: rcs.Private,
|
|
}, nil
|
|
}
|
|
|
|
// DeepCopy creates a copy of the receiver where any pointers to nested mutable
|
|
// values are also copied, thus ensuring that future mutations of the receiver
|
|
// will not affect the copy.
|
|
//
|
|
// Some types used within a resource change are immutable by convention even
|
|
// though the Go language allows them to be mutated, such as the types from
|
|
// the addrs package. These are _not_ copied by this method, under the
|
|
// assumption that callers will behave themselves.
|
|
func (rcs *ResourceInstanceChangeSrc) DeepCopy() *ResourceInstanceChangeSrc {
|
|
if rcs == nil {
|
|
return nil
|
|
}
|
|
ret := *rcs
|
|
|
|
ret.RequiredReplace = cty.NewPathSet(ret.RequiredReplace.List()...)
|
|
|
|
if len(ret.Private) != 0 {
|
|
private := make([]byte, len(ret.Private))
|
|
copy(private, ret.Private)
|
|
ret.Private = private
|
|
}
|
|
|
|
ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
|
|
ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
|
|
|
|
return &ret
|
|
}
|
|
|
|
func (rcs *ResourceInstanceChangeSrc) Moved() bool {
|
|
return !rcs.Addr.Equal(rcs.PrevRunAddr)
|
|
}
|
|
|
|
// OutputChangeSrc describes a change to an output value.
|
|
type OutputChangeSrc struct {
|
|
// Addr is the absolute address of the output value that the change
|
|
// will apply to.
|
|
Addr addrs.AbsOutputValue
|
|
|
|
// ChangeSrc is an embedded description of the not-yet-decoded change.
|
|
//
|
|
// For output value changes, the type constraint for the DynamicValue
|
|
// instances is always cty.DynamicPseudoType.
|
|
ChangeSrc
|
|
|
|
// Sensitive, if true, indicates that either the old or new value in the
|
|
// change is sensitive and so a rendered version of the plan in the UI
|
|
// should elide the actual values while still indicating the action of the
|
|
// change.
|
|
Sensitive bool
|
|
}
|
|
|
|
// Decode unmarshals the raw representation of the output value being
|
|
// changed.
|
|
func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) {
|
|
change, err := ocs.ChangeSrc.Decode(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &OutputChange{
|
|
Addr: ocs.Addr,
|
|
Change: *change,
|
|
Sensitive: ocs.Sensitive,
|
|
}, nil
|
|
}
|
|
|
|
// DeepCopy creates a copy of the receiver where any pointers to nested mutable
|
|
// values are also copied, thus ensuring that future mutations of the receiver
|
|
// will not affect the copy.
|
|
//
|
|
// Some types used within a resource change are immutable by convention even
|
|
// though the Go language allows them to be mutated, such as the types from
|
|
// the addrs package. These are _not_ copied by this method, under the
|
|
// assumption that callers will behave themselves.
|
|
func (ocs *OutputChangeSrc) DeepCopy() *OutputChangeSrc {
|
|
if ocs == nil {
|
|
return nil
|
|
}
|
|
ret := *ocs
|
|
|
|
ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
|
|
ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
|
|
|
|
return &ret
|
|
}
|
|
|
|
// ImportingSrc is the part of a ChangeSrc that describes the embedded import
|
|
// action.
|
|
//
|
|
// The fields in here are subject to change, so downstream consumers should be
|
|
// prepared for backwards compatibility in case the contents changes.
|
|
type ImportingSrc struct {
|
|
// ID is the original ID of the imported resource.
|
|
ID string
|
|
|
|
// Identity is the original identity of the imported resource.
|
|
Identity DynamicValue
|
|
|
|
// Unknown is true if the ID was unknown when we tried to import it. This
|
|
// should only be true if the overall change is embedded within a deferred
|
|
// action.
|
|
Unknown bool
|
|
}
|
|
|
|
// Decode unmarshals the raw representation of the importing action.
|
|
func (is *ImportingSrc) Decode(identityType cty.Type) *Importing {
|
|
if is == nil {
|
|
return nil
|
|
}
|
|
if is.Unknown {
|
|
if is.Identity == nil {
|
|
return &Importing{
|
|
Target: cty.UnknownVal(cty.String),
|
|
}
|
|
}
|
|
|
|
return &Importing{
|
|
Target: cty.UnknownVal(cty.EmptyObject),
|
|
}
|
|
}
|
|
|
|
if is.Identity == nil {
|
|
return &Importing{
|
|
Target: cty.StringVal(is.ID),
|
|
}
|
|
}
|
|
|
|
target, err := is.Identity.Decode(identityType)
|
|
if err != nil {
|
|
return &Importing{
|
|
Target: target,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ChangeSrc is a not-yet-decoded Change.
|
|
type ChangeSrc struct {
|
|
// Action defines what kind of change is being made.
|
|
Action Action
|
|
|
|
// Before and After correspond to the fields of the same name in Change,
|
|
// but have not yet been decoded from the serialized value used for
|
|
// storage.
|
|
Before, After DynamicValue
|
|
|
|
// BeforeIdentity and AfterIdentity correspond to the fields of the same name in Change,
|
|
// but have not yet been decoded from the serialized value used for
|
|
// storage.
|
|
BeforeIdentity, AfterIdentity DynamicValue
|
|
|
|
// BeforeSensitivePaths and AfterSensitivePaths are the paths for any
|
|
// values in Before or After (respectively) that are considered to be
|
|
// sensitive. The sensitive marks are removed from the in-memory values
|
|
// to enable encoding (marked values cannot be marshalled), and so we
|
|
// store the sensitive paths to allow re-marking later when we decode
|
|
// the serialized change.
|
|
BeforeSensitivePaths, AfterSensitivePaths []cty.Path
|
|
|
|
// Importing is present if the resource is being imported as part of this
|
|
// change.
|
|
//
|
|
// Use the simple presence of this field to detect if a ChangeSrc is to be
|
|
// imported, the contents of this structure may be modified going forward.
|
|
Importing *ImportingSrc
|
|
|
|
// GeneratedConfig contains any HCL config generated for this resource
|
|
// during planning, as a string. If GeneratedConfig is populated, Importing
|
|
// should be true. However, not all Importing changes contain generated
|
|
// config.
|
|
GeneratedConfig string
|
|
}
|
|
|
|
// Decode unmarshals the raw representations of the before and after values
|
|
// to produce a Change object. Pass the type constraint that the result must
|
|
// conform to.
|
|
//
|
|
// Where a ChangeSrc is embedded in some other struct, it's generally better
|
|
// to call the corresponding Decode method of that struct rather than working
|
|
// directly with its embedded Change.
|
|
func (cs *ChangeSrc) Decode(schema *providers.Schema) (*Change, error) {
|
|
var err error
|
|
|
|
ty := cty.DynamicPseudoType
|
|
identityType := cty.DynamicPseudoType
|
|
if schema != nil {
|
|
ty = schema.Body.ImpliedType()
|
|
identityType = schema.Identity.ImpliedType()
|
|
}
|
|
|
|
before := cty.NullVal(ty)
|
|
beforeIdentity := cty.NullVal(identityType)
|
|
after := cty.NullVal(ty)
|
|
afterIdentity := cty.NullVal(identityType)
|
|
|
|
if len(cs.Before) > 0 {
|
|
before, err = cs.Before.Decode(ty)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'before' value: %s", err)
|
|
}
|
|
}
|
|
if len(cs.BeforeIdentity) > 0 {
|
|
beforeIdentity, err = cs.BeforeIdentity.Decode(identityType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'beforeIdentity' value: %s", err)
|
|
}
|
|
}
|
|
if len(cs.After) > 0 {
|
|
after, err = cs.After.Decode(ty)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'after' value: %s", err)
|
|
}
|
|
}
|
|
if len(cs.AfterIdentity) > 0 {
|
|
afterIdentity, err = cs.AfterIdentity.Decode(identityType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'afterIdentity' value: %s", err)
|
|
}
|
|
}
|
|
|
|
return &Change{
|
|
Action: cs.Action,
|
|
Before: marks.MarkPaths(before, marks.Sensitive, cs.BeforeSensitivePaths),
|
|
BeforeIdentity: beforeIdentity,
|
|
After: marks.MarkPaths(after, marks.Sensitive, cs.AfterSensitivePaths),
|
|
AfterIdentity: afterIdentity,
|
|
Importing: cs.Importing.Decode(identityType),
|
|
GeneratedConfig: cs.GeneratedConfig,
|
|
}, nil
|
|
}
|
|
|
|
// AppendActionInvocationInstanceChange records the given resource instance change in
|
|
// the set of planned resource changes.
|
|
func (c *ChangesSrc) AppendActionInvocationInstanceChange(action *ActionInvocationInstanceSrc) {
|
|
if c == nil {
|
|
panic("AppendActionInvocationInstanceChange on nil ChangesSync")
|
|
}
|
|
|
|
a := action.DeepCopy()
|
|
c.ActionInvocations = append(c.ActionInvocations, a)
|
|
}
|
|
|
|
type ActionInvocationInstanceSrc struct {
|
|
Addr addrs.AbsActionInstance
|
|
ActionTrigger ActionTrigger
|
|
|
|
ConfigValue DynamicValue
|
|
SensitiveConfigPaths []cty.Path
|
|
|
|
ProviderAddr addrs.AbsProviderConfig
|
|
}
|
|
|
|
// Decode unmarshals the raw representation of actions.
|
|
func (acs *ActionInvocationInstanceSrc) Decode(schema *providers.ActionSchema) (*ActionInvocationInstance, error) {
|
|
ty := cty.DynamicPseudoType
|
|
if schema != nil {
|
|
ty = schema.ConfigSchema.ImpliedType()
|
|
}
|
|
|
|
config, err := acs.ConfigValue.Decode(ty)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'config' value: %s", err)
|
|
}
|
|
markedConfigValue := marks.MarkPaths(config, marks.Sensitive, acs.SensitiveConfigPaths)
|
|
|
|
ai := &ActionInvocationInstance{
|
|
Addr: acs.Addr,
|
|
ActionTrigger: acs.ActionTrigger,
|
|
ProviderAddr: acs.ProviderAddr,
|
|
ConfigValue: markedConfigValue,
|
|
}
|
|
return ai, nil
|
|
}
|
|
|
|
func (acs *ActionInvocationInstanceSrc) DeepCopy() *ActionInvocationInstanceSrc {
|
|
if acs == nil {
|
|
return acs
|
|
}
|
|
ret := *acs
|
|
ret.ConfigValue = ret.ConfigValue.Copy()
|
|
return &ret
|
|
}
|
|
|
|
func (acs *ActionInvocationInstanceSrc) Less(other *ActionInvocationInstanceSrc) bool {
|
|
if acs.ActionTrigger.Equals(other.ActionTrigger) {
|
|
return acs.Addr.Less(other.Addr)
|
|
}
|
|
return acs.ActionTrigger.Less(other.ActionTrigger)
|
|
}
|
|
|
|
// FilterLaterActionInvocations returns the list of action invocations that
|
|
// should be triggered after this one. This function assumes the supplied list
|
|
// of action invocations has already been filtered to invocations against the
|
|
// same resource as the current invocation.
|
|
func (acs *ActionInvocationInstanceSrc) FilterLaterActionInvocations(actionInvocations []*ActionInvocationInstanceSrc) []*ActionInvocationInstanceSrc {
|
|
needleLat := acs.ActionTrigger.(*LifecycleActionTrigger)
|
|
|
|
var laterInvocations []*ActionInvocationInstanceSrc
|
|
for _, invocation := range actionInvocations {
|
|
if lat, ok := invocation.ActionTrigger.(*LifecycleActionTrigger); ok {
|
|
if sameTriggerEvent(lat, needleLat) && triggersLater(lat, needleLat) {
|
|
laterInvocations = append(laterInvocations, invocation)
|
|
}
|
|
}
|
|
}
|
|
return laterInvocations
|
|
}
|
|
|
|
func sameTriggerEvent(one, two *LifecycleActionTrigger) bool {
|
|
return one.ActionTriggerEvent == two.ActionTriggerEvent
|
|
}
|
|
|
|
func triggersLater(one, two *LifecycleActionTrigger) bool {
|
|
return one.ActionTriggerBlockIndex > two.ActionTriggerBlockIndex || (one.ActionTriggerBlockIndex == two.ActionTriggerBlockIndex && one.ActionsListIndex > two.ActionsListIndex)
|
|
}
|