github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/plans/changes_src.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package plans
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/terramate-io/tf/addrs"
    10  	"github.com/terramate-io/tf/states"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  // ResourceInstanceChangeSrc is a not-yet-decoded ResourceInstanceChange.
    15  // Pass the associated resource type's schema type to method Decode to
    16  // obtain a ResourceInstanceChange.
    17  type ResourceInstanceChangeSrc struct {
    18  	// Addr is the absolute address of the resource instance that the change
    19  	// will apply to.
    20  	Addr addrs.AbsResourceInstance
    21  
    22  	// PrevRunAddr is the absolute address that this resource instance had at
    23  	// the conclusion of a previous run.
    24  	//
    25  	// This will typically be the same as Addr, but can be different if the
    26  	// previous resource instance was subject to a "moved" block that we
    27  	// handled in the process of creating this plan.
    28  	//
    29  	// For the initial creation of a resource instance there isn't really any
    30  	// meaningful "previous run address", but PrevRunAddr will still be set
    31  	// equal to Addr in that case in order to simplify logic elsewhere which
    32  	// aims to detect and react to the movement of instances between addresses.
    33  	PrevRunAddr addrs.AbsResourceInstance
    34  
    35  	// DeposedKey is the identifier for a deposed object associated with the
    36  	// given instance, or states.NotDeposed if this change applies to the
    37  	// current object.
    38  	//
    39  	// A Replace change for a resource with create_before_destroy set will
    40  	// create a new DeposedKey temporarily during replacement. In that case,
    41  	// DeposedKey in the plan is always states.NotDeposed, representing that
    42  	// the current object is being replaced with the deposed.
    43  	DeposedKey states.DeposedKey
    44  
    45  	// Provider is the address of the provider configuration that was used
    46  	// to plan this change, and thus the configuration that must also be
    47  	// used to apply it.
    48  	ProviderAddr addrs.AbsProviderConfig
    49  
    50  	// ChangeSrc is an embedded description of the not-yet-decoded change.
    51  	ChangeSrc
    52  
    53  	// ActionReason is an optional extra indication of why we chose the
    54  	// action recorded in Change.Action for this particular resource instance.
    55  	//
    56  	// This is an approximate mechanism only for the purpose of explaining the
    57  	// plan to end-users in the UI and is not to be used for any
    58  	// decision-making during the apply step; if apply behavior needs to vary
    59  	// depending on the "action reason" then the information for that decision
    60  	// must be recorded more precisely elsewhere for that purpose.
    61  	//
    62  	// See the field of the same name in ResourceInstanceChange for more
    63  	// details.
    64  	ActionReason ResourceInstanceChangeActionReason
    65  
    66  	// RequiredReplace is a set of paths that caused the change action to be
    67  	// Replace rather than Update. Always nil if the change action is not
    68  	// Replace.
    69  	RequiredReplace cty.PathSet
    70  
    71  	// Private allows a provider to stash any extra data that is opaque to
    72  	// Terraform that relates to this change. Terraform will save this
    73  	// byte-for-byte and return it to the provider in the apply call.
    74  	Private []byte
    75  }
    76  
    77  // Decode unmarshals the raw representation of the instance object being
    78  // changed. Pass the implied type of the corresponding resource type schema
    79  // for correct operation.
    80  func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChange, error) {
    81  	change, err := rcs.ChangeSrc.Decode(ty)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	prevRunAddr := rcs.PrevRunAddr
    86  	if prevRunAddr.Resource.Resource.Type == "" {
    87  		// Suggests an old caller that hasn't been properly updated to
    88  		// populate this yet.
    89  		prevRunAddr = rcs.Addr
    90  	}
    91  	return &ResourceInstanceChange{
    92  		Addr:            rcs.Addr,
    93  		PrevRunAddr:     prevRunAddr,
    94  		DeposedKey:      rcs.DeposedKey,
    95  		ProviderAddr:    rcs.ProviderAddr,
    96  		Change:          *change,
    97  		ActionReason:    rcs.ActionReason,
    98  		RequiredReplace: rcs.RequiredReplace,
    99  		Private:         rcs.Private,
   100  	}, nil
   101  }
   102  
   103  // DeepCopy creates a copy of the receiver where any pointers to nested mutable
   104  // values are also copied, thus ensuring that future mutations of the receiver
   105  // will not affect the copy.
   106  //
   107  // Some types used within a resource change are immutable by convention even
   108  // though the Go language allows them to be mutated, such as the types from
   109  // the addrs package. These are _not_ copied by this method, under the
   110  // assumption that callers will behave themselves.
   111  func (rcs *ResourceInstanceChangeSrc) DeepCopy() *ResourceInstanceChangeSrc {
   112  	if rcs == nil {
   113  		return nil
   114  	}
   115  	ret := *rcs
   116  
   117  	ret.RequiredReplace = cty.NewPathSet(ret.RequiredReplace.List()...)
   118  
   119  	if len(ret.Private) != 0 {
   120  		private := make([]byte, len(ret.Private))
   121  		copy(private, ret.Private)
   122  		ret.Private = private
   123  	}
   124  
   125  	ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
   126  	ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
   127  
   128  	return &ret
   129  }
   130  
   131  func (rcs *ResourceInstanceChangeSrc) Moved() bool {
   132  	return !rcs.Addr.Equal(rcs.PrevRunAddr)
   133  }
   134  
   135  // OutputChangeSrc describes a change to an output value.
   136  type OutputChangeSrc struct {
   137  	// Addr is the absolute address of the output value that the change
   138  	// will apply to.
   139  	Addr addrs.AbsOutputValue
   140  
   141  	// ChangeSrc is an embedded description of the not-yet-decoded change.
   142  	//
   143  	// For output value changes, the type constraint for the DynamicValue
   144  	// instances is always cty.DynamicPseudoType.
   145  	ChangeSrc
   146  
   147  	// Sensitive, if true, indicates that either the old or new value in the
   148  	// change is sensitive and so a rendered version of the plan in the UI
   149  	// should elide the actual values while still indicating the action of the
   150  	// change.
   151  	Sensitive bool
   152  }
   153  
   154  // Decode unmarshals the raw representation of the output value being
   155  // changed.
   156  func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) {
   157  	change, err := ocs.ChangeSrc.Decode(cty.DynamicPseudoType)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return &OutputChange{
   162  		Addr:      ocs.Addr,
   163  		Change:    *change,
   164  		Sensitive: ocs.Sensitive,
   165  	}, nil
   166  }
   167  
   168  // DeepCopy creates a copy of the receiver where any pointers to nested mutable
   169  // values are also copied, thus ensuring that future mutations of the receiver
   170  // will not affect the copy.
   171  //
   172  // Some types used within a resource change are immutable by convention even
   173  // though the Go language allows them to be mutated, such as the types from
   174  // the addrs package. These are _not_ copied by this method, under the
   175  // assumption that callers will behave themselves.
   176  func (ocs *OutputChangeSrc) DeepCopy() *OutputChangeSrc {
   177  	if ocs == nil {
   178  		return nil
   179  	}
   180  	ret := *ocs
   181  
   182  	ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
   183  	ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
   184  
   185  	return &ret
   186  }
   187  
   188  // ImportingSrc is the part of a ChangeSrc that describes the embedded import
   189  // action.
   190  //
   191  // The fields in here are subject to change, so downstream consumers should be
   192  // prepared for backwards compatibility in case the contents changes.
   193  type ImportingSrc struct {
   194  	// ID is the original ID of the imported resource.
   195  	ID string
   196  }
   197  
   198  // ChangeSrc is a not-yet-decoded Change.
   199  type ChangeSrc struct {
   200  	// Action defines what kind of change is being made.
   201  	Action Action
   202  
   203  	// Before and After correspond to the fields of the same name in Change,
   204  	// but have not yet been decoded from the serialized value used for
   205  	// storage.
   206  	Before, After DynamicValue
   207  
   208  	// BeforeValMarks and AfterValMarks are stored path+mark combinations
   209  	// that might be discovered when encoding a change. Marks are removed
   210  	// to enable encoding (marked values cannot be marshalled), and so storing
   211  	// the path+mark combinations allow us to re-mark the value later
   212  	// when, for example, displaying the diff to the UI.
   213  	BeforeValMarks, AfterValMarks []cty.PathValueMarks
   214  
   215  	// Importing is present if the resource is being imported as part of this
   216  	// change.
   217  	//
   218  	// Use the simple presence of this field to detect if a ChangeSrc is to be
   219  	// imported, the contents of this structure may be modified going forward.
   220  	Importing *ImportingSrc
   221  
   222  	// GeneratedConfig contains any HCL config generated for this resource
   223  	// during planning, as a string. If GeneratedConfig is populated, Importing
   224  	// should be true. However, not all Importing changes contain generated
   225  	// config.
   226  	GeneratedConfig string
   227  }
   228  
   229  // Decode unmarshals the raw representations of the before and after values
   230  // to produce a Change object. Pass the type constraint that the result must
   231  // conform to.
   232  //
   233  // Where a ChangeSrc is embedded in some other struct, it's generally better
   234  // to call the corresponding Decode method of that struct rather than working
   235  // directly with its embedded Change.
   236  func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) {
   237  	var err error
   238  	before := cty.NullVal(ty)
   239  	after := cty.NullVal(ty)
   240  
   241  	if len(cs.Before) > 0 {
   242  		before, err = cs.Before.Decode(ty)
   243  		if err != nil {
   244  			return nil, fmt.Errorf("error decoding 'before' value: %s", err)
   245  		}
   246  	}
   247  	if len(cs.After) > 0 {
   248  		after, err = cs.After.Decode(ty)
   249  		if err != nil {
   250  			return nil, fmt.Errorf("error decoding 'after' value: %s", err)
   251  		}
   252  	}
   253  
   254  	var importing *Importing
   255  	if cs.Importing != nil {
   256  		importing = &Importing{ID: cs.Importing.ID}
   257  	}
   258  
   259  	return &Change{
   260  		Action:          cs.Action,
   261  		Before:          before.MarkWithPaths(cs.BeforeValMarks),
   262  		After:           after.MarkWithPaths(cs.AfterValMarks),
   263  		Importing:       importing,
   264  		GeneratedConfig: cs.GeneratedConfig,
   265  	}, nil
   266  }