github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plans/changes.go (about)

     1  package plans
     2  
     3  import (
     4  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     5  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
     6  	"github.com/zclconf/go-cty/cty"
     7  )
     8  
     9  // Changes describes various actions that Terraform will attempt to take if
    10  // the corresponding plan is applied.
    11  //
    12  // A Changes object can be rendered into a visual diff (by the caller, using
    13  // code in another package) for display to the user.
    14  type Changes struct {
    15  	// Resources tracks planned changes to resource instance objects.
    16  	Resources []*ResourceInstanceChangeSrc
    17  
    18  	// Outputs tracks planned changes output values.
    19  	//
    20  	// Note that although an in-memory plan contains planned changes for
    21  	// outputs throughout the configuration, a plan serialized
    22  	// to disk retains only the root outputs because they are
    23  	// externally-visible, while other outputs are implementation details and
    24  	// can be easily re-calculated during the apply phase. Therefore only root
    25  	// module outputs will survive a round-trip through a plan file.
    26  	Outputs []*OutputChangeSrc
    27  }
    28  
    29  // NewChanges returns a valid Changes object that describes no changes.
    30  func NewChanges() *Changes {
    31  	return &Changes{}
    32  }
    33  
    34  func (c *Changes) Empty() bool {
    35  	for _, res := range c.Resources {
    36  		if res.Action != NoOp {
    37  			return false
    38  		}
    39  	}
    40  	return true
    41  }
    42  
    43  // ResourceInstance returns the planned change for the current object of the
    44  // resource instance of the given address, if any. Returns nil if no change is
    45  // planned.
    46  func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc {
    47  	addrStr := addr.String()
    48  	for _, rc := range c.Resources {
    49  		if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed {
    50  			return rc
    51  		}
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  // ResourceInstanceDeposed returns the plan change of a deposed object of
    58  // the resource instance of the given address, if any. Returns nil if no change
    59  // is planned.
    60  func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc {
    61  	addrStr := addr.String()
    62  	for _, rc := range c.Resources {
    63  		if rc.Addr.String() == addrStr && rc.DeposedKey == key {
    64  			return rc
    65  		}
    66  	}
    67  
    68  	return nil
    69  }
    70  
    71  // OutputValue returns the planned change for the output value with the
    72  //  given address, if any. Returns nil if no change is planned.
    73  func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc {
    74  	addrStr := addr.String()
    75  	for _, oc := range c.Outputs {
    76  		if oc.Addr.String() == addrStr {
    77  			return oc
    78  		}
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // SyncWrapper returns a wrapper object around the receiver that can be used
    85  // to make certain changes to the receiver in a concurrency-safe way, as long
    86  // as all callers share the same wrapper object.
    87  func (c *Changes) SyncWrapper() *ChangesSync {
    88  	return &ChangesSync{
    89  		changes: c,
    90  	}
    91  }
    92  
    93  // ResourceInstanceChange describes a change to a particular resource instance
    94  // object.
    95  type ResourceInstanceChange struct {
    96  	// Addr is the absolute address of the resource instance that the change
    97  	// will apply to.
    98  	Addr addrs.AbsResourceInstance
    99  
   100  	// DeposedKey is the identifier for a deposed object associated with the
   101  	// given instance, or states.NotDeposed if this change applies to the
   102  	// current object.
   103  	//
   104  	// A Replace change for a resource with create_before_destroy set will
   105  	// create a new DeposedKey temporarily during replacement. In that case,
   106  	// DeposedKey in the plan is always states.NotDeposed, representing that
   107  	// the current object is being replaced with the deposed.
   108  	DeposedKey states.DeposedKey
   109  
   110  	// Provider is the address of the provider configuration that was used
   111  	// to plan this change, and thus the configuration that must also be
   112  	// used to apply it.
   113  	ProviderAddr addrs.AbsProviderConfig
   114  
   115  	// Change is an embedded description of the change.
   116  	Change
   117  
   118  	// RequiredReplace is a set of paths that caused the change action to be
   119  	// Replace rather than Update. Always nil if the change action is not
   120  	// Replace.
   121  	//
   122  	// This is retained only for UI-plan-rendering purposes and so it does not
   123  	// currently survive a round-trip through a saved plan file.
   124  	RequiredReplace cty.PathSet
   125  
   126  	// Private allows a provider to stash any extra data that is opaque to
   127  	// Terraform that relates to this change. Terraform will save this
   128  	// byte-for-byte and return it to the provider in the apply call.
   129  	Private []byte
   130  }
   131  
   132  // Encode produces a variant of the reciever that has its change values
   133  // serialized so it can be written to a plan file. Pass the implied type of the
   134  // corresponding resource type schema for correct operation.
   135  func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) {
   136  	cs, err := rc.Change.Encode(ty)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return &ResourceInstanceChangeSrc{
   141  		Addr:            rc.Addr,
   142  		DeposedKey:      rc.DeposedKey,
   143  		ProviderAddr:    rc.ProviderAddr,
   144  		ChangeSrc:       *cs,
   145  		RequiredReplace: rc.RequiredReplace,
   146  		Private:         rc.Private,
   147  	}, err
   148  }
   149  
   150  // Simplify will, where possible, produce a change with a simpler action than
   151  // the receiever given a flag indicating whether the caller is dealing with
   152  // a normal apply or a destroy. This flag deals with the fact that Terraform
   153  // Core uses a specialized graph node type for destroying; only that
   154  // specialized node should set "destroying" to true.
   155  //
   156  // The following table shows the simplification behavior:
   157  //
   158  //     Action    Destroying?   New Action
   159  //     --------+-------------+-----------
   160  //     Create    true          NoOp
   161  //     Delete    false         NoOp
   162  //     Replace   true          Delete
   163  //     Replace   false         Create
   164  //
   165  // For any combination not in the above table, the Simplify just returns the
   166  // receiver as-is.
   167  func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange {
   168  	if destroying {
   169  		switch rc.Action {
   170  		case Delete:
   171  			// We'll fall out and just return rc verbatim, then.
   172  		case CreateThenDelete, DeleteThenCreate:
   173  			return &ResourceInstanceChange{
   174  				Addr:         rc.Addr,
   175  				DeposedKey:   rc.DeposedKey,
   176  				Private:      rc.Private,
   177  				ProviderAddr: rc.ProviderAddr,
   178  				Change: Change{
   179  					Action: Delete,
   180  					Before: rc.Before,
   181  					After:  cty.NullVal(rc.Before.Type()),
   182  				},
   183  			}
   184  		default:
   185  			return &ResourceInstanceChange{
   186  				Addr:         rc.Addr,
   187  				DeposedKey:   rc.DeposedKey,
   188  				Private:      rc.Private,
   189  				ProviderAddr: rc.ProviderAddr,
   190  				Change: Change{
   191  					Action: NoOp,
   192  					Before: rc.Before,
   193  					After:  rc.Before,
   194  				},
   195  			}
   196  		}
   197  	} else {
   198  		switch rc.Action {
   199  		case Delete:
   200  			return &ResourceInstanceChange{
   201  				Addr:         rc.Addr,
   202  				DeposedKey:   rc.DeposedKey,
   203  				Private:      rc.Private,
   204  				ProviderAddr: rc.ProviderAddr,
   205  				Change: Change{
   206  					Action: NoOp,
   207  					Before: rc.Before,
   208  					After:  rc.Before,
   209  				},
   210  			}
   211  		case CreateThenDelete, DeleteThenCreate:
   212  			return &ResourceInstanceChange{
   213  				Addr:         rc.Addr,
   214  				DeposedKey:   rc.DeposedKey,
   215  				Private:      rc.Private,
   216  				ProviderAddr: rc.ProviderAddr,
   217  				Change: Change{
   218  					Action: Create,
   219  					Before: cty.NullVal(rc.After.Type()),
   220  					After:  rc.After,
   221  				},
   222  			}
   223  		}
   224  	}
   225  
   226  	// If we fall out here then our change is already simple enough.
   227  	return rc
   228  }
   229  
   230  // OutputChange describes a change to an output value.
   231  type OutputChange struct {
   232  	// Addr is the absolute address of the output value that the change
   233  	// will apply to.
   234  	Addr addrs.AbsOutputValue
   235  
   236  	// Change is an embedded description of the change.
   237  	//
   238  	// For output value changes, the type constraint for the DynamicValue
   239  	// instances is always cty.DynamicPseudoType.
   240  	Change
   241  
   242  	// Sensitive, if true, indicates that either the old or new value in the
   243  	// change is sensitive and so a rendered version of the plan in the UI
   244  	// should elide the actual values while still indicating the action of the
   245  	// change.
   246  	Sensitive bool
   247  }
   248  
   249  // Encode produces a variant of the reciever that has its change values
   250  // serialized so it can be written to a plan file.
   251  func (oc *OutputChange) Encode() (*OutputChangeSrc, error) {
   252  	cs, err := oc.Change.Encode(cty.DynamicPseudoType)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	return &OutputChangeSrc{
   257  		Addr:      oc.Addr,
   258  		ChangeSrc: *cs,
   259  		Sensitive: oc.Sensitive,
   260  	}, err
   261  }
   262  
   263  // Change describes a single change with a given action.
   264  type Change struct {
   265  	// Action defines what kind of change is being made.
   266  	Action Action
   267  
   268  	// Interpretation of Before and After depend on Action:
   269  	//
   270  	//     NoOp     Before and After are the same, unchanged value
   271  	//     Create   Before is nil, and After is the expected value after create.
   272  	//     Read     Before is any prior value (nil if no prior), and After is the
   273  	//              value that was or will be read.
   274  	//     Update   Before is the value prior to update, and After is the expected
   275  	//              value after update.
   276  	//     Replace  As with Update.
   277  	//     Delete   Before is the value prior to delete, and After is always nil.
   278  	//
   279  	// Unknown values may appear anywhere within the Before and After values,
   280  	// either as the values themselves or as nested elements within known
   281  	// collections/structures.
   282  	Before, After cty.Value
   283  }
   284  
   285  // Encode produces a variant of the reciever that has its change values
   286  // serialized so it can be written to a plan file. Pass the type constraint
   287  // that the values are expected to conform to; to properly decode the values
   288  // later an identical type constraint must be provided at that time.
   289  //
   290  // Where a Change is embedded in some other struct, it's generally better
   291  // to call the corresponding Encode method of that struct rather than working
   292  // directly with its embedded Change.
   293  func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
   294  	beforeDV, err := NewDynamicValue(c.Before, ty)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	afterDV, err := NewDynamicValue(c.After, ty)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	return &ChangeSrc{
   304  		Action: c.Action,
   305  		Before: beforeDV,
   306  		After:  afterDV,
   307  	}, nil
   308  }