mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-23 11:44:26 -05:00
In order to expose the effect of any relevant "moved" statements we dealt with prior to creating the plan, we'll record with each ResourceInstanceChange both is current address and the address it was tracked at for the previous run. To save consumers of these objects from having to special-case the situation where there _was_ no previous run (e.g. because this is a Create change), we'll just pretend the previous run address was the same as the current address in that case, the same as for an update without any renaming in effect. This includes a breaking change to the plan file format, but one that doesn't require a version number increment because there is no ambiguity between the two formats and so mismatched parsers will already fail with an error message. As of this commit we've just added the new field but not yet populated it with any useful information: it always just matches Addr. A future commit will wire this up to the result of applying the moves so that we can populate it correctly. We also don't yet expose this new information anywhere in the UI layer.
230 lines
7.9 KiB
Go
230 lines
7.9 KiB
Go
package plans
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
"github.com/hashicorp/terraform/internal/states"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// 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.
|
|
Addr addrs.AbsResourceInstance
|
|
|
|
// 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
|
|
|
|
// 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
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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(ty cty.Type) (*ResourceInstanceChange, error) {
|
|
change, err := rcs.ChangeSrc.Decode(ty)
|
|
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
|
|
}
|
|
|
|
// 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(cty.DynamicPseudoType)
|
|
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
|
|
}
|
|
|
|
// 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
|
|
|
|
// BeforeValMarks and AfterValMarks are stored path+mark combinations
|
|
// that might be discovered when encoding a change. Marks are removed
|
|
// to enable encoding (marked values cannot be marshalled), and so storing
|
|
// the path+mark combinations allow us to re-mark the value later
|
|
// when, for example, displaying the diff to the UI.
|
|
BeforeValMarks, AfterValMarks []cty.PathValueMarks
|
|
}
|
|
|
|
// 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(ty cty.Type) (*Change, error) {
|
|
var err error
|
|
before := cty.NullVal(ty)
|
|
after := cty.NullVal(ty)
|
|
|
|
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.After) > 0 {
|
|
after, err = cs.After.Decode(ty)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error decoding 'after' value: %s", err)
|
|
}
|
|
}
|
|
|
|
return &Change{
|
|
Action: cs.Action,
|
|
Before: before.MarkWithPaths(cs.BeforeValMarks),
|
|
After: after.MarkWithPaths(cs.AfterValMarks),
|
|
}, nil
|
|
}
|