github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/plans/changes.go (about)

     1  package plans
     2  
     3  import (
     4  	"github.com/cycloidio/terraform/addrs"
     5  	"github.com/cycloidio/terraform/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 || res.Moved() {
    37  			return false
    38  		}
    39  	}
    40  
    41  	for _, out := range c.Outputs {
    42  		if out.Addr.Module.IsRoot() && out.Action != NoOp {
    43  			return false
    44  		}
    45  	}
    46  
    47  	return true
    48  }
    49  
    50  // ResourceInstance returns the planned change for the current object of the
    51  // resource instance of the given address, if any. Returns nil if no change is
    52  // planned.
    53  func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc {
    54  	for _, rc := range c.Resources {
    55  		if rc.Addr.Equal(addr) && rc.DeposedKey == states.NotDeposed {
    56  			return rc
    57  		}
    58  	}
    59  
    60  	return nil
    61  
    62  }
    63  
    64  // InstancesForConfigResource returns the planned change for the current objects
    65  // of the resource instances of the given address, if any. Returns nil if no
    66  // changes are planned.
    67  func (c *Changes) InstancesForConfigResource(addr addrs.ConfigResource) []*ResourceInstanceChangeSrc {
    68  	var changes []*ResourceInstanceChangeSrc
    69  	for _, rc := range c.Resources {
    70  		resAddr := rc.Addr.ContainingResource().Config()
    71  		if resAddr.Equal(addr) && rc.DeposedKey == states.NotDeposed {
    72  			changes = append(changes, rc)
    73  		}
    74  	}
    75  
    76  	return changes
    77  }
    78  
    79  // ResourceInstanceDeposed returns the plan change of a deposed object of
    80  // the resource instance of the given address, if any. Returns nil if no change
    81  // is planned.
    82  func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc {
    83  	for _, rc := range c.Resources {
    84  		if rc.Addr.Equal(addr) && rc.DeposedKey == key {
    85  			return rc
    86  		}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // OutputValue returns the planned change for the output value with the
    93  //  given address, if any. Returns nil if no change is planned.
    94  func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc {
    95  	for _, oc := range c.Outputs {
    96  		if oc.Addr.Equal(addr) {
    97  			return oc
    98  		}
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // OutputValues returns planned changes for all outputs for all module
   105  // instances that reside in the parent path.  Returns nil if no changes are
   106  // planned.
   107  func (c *Changes) OutputValues(parent addrs.ModuleInstance, module addrs.ModuleCall) []*OutputChangeSrc {
   108  	var res []*OutputChangeSrc
   109  
   110  	for _, oc := range c.Outputs {
   111  		// we can't evaluate root module outputs
   112  		if oc.Addr.Module.Equal(addrs.RootModuleInstance) {
   113  			continue
   114  		}
   115  
   116  		changeMod, changeCall := oc.Addr.Module.Call()
   117  		// this does not reside on our parent instance path
   118  		if !changeMod.Equal(parent) {
   119  			continue
   120  		}
   121  
   122  		// this is not the module you're looking for
   123  		if changeCall.Name != module.Name {
   124  			continue
   125  		}
   126  
   127  		res = append(res, oc)
   128  
   129  	}
   130  
   131  	return res
   132  }
   133  
   134  // SyncWrapper returns a wrapper object around the receiver that can be used
   135  // to make certain changes to the receiver in a concurrency-safe way, as long
   136  // as all callers share the same wrapper object.
   137  func (c *Changes) SyncWrapper() *ChangesSync {
   138  	return &ChangesSync{
   139  		changes: c,
   140  	}
   141  }
   142  
   143  // ResourceInstanceChange describes a change to a particular resource instance
   144  // object.
   145  type ResourceInstanceChange struct {
   146  	// Addr is the absolute address of the resource instance that the change
   147  	// will apply to.
   148  	Addr addrs.AbsResourceInstance
   149  
   150  	// PrevRunAddr is the absolute address that this resource instance had at
   151  	// the conclusion of a previous run.
   152  	//
   153  	// This will typically be the same as Addr, but can be different if the
   154  	// previous resource instance was subject to a "moved" block that we
   155  	// handled in the process of creating this plan.
   156  	//
   157  	// For the initial creation of a resource instance there isn't really any
   158  	// meaningful "previous run address", but PrevRunAddr will still be set
   159  	// equal to Addr in that case in order to simplify logic elsewhere which
   160  	// aims to detect and react to the movement of instances between addresses.
   161  	PrevRunAddr addrs.AbsResourceInstance
   162  
   163  	// DeposedKey is the identifier for a deposed object associated with the
   164  	// given instance, or states.NotDeposed if this change applies to the
   165  	// current object.
   166  	//
   167  	// A Replace change for a resource with create_before_destroy set will
   168  	// create a new DeposedKey temporarily during replacement. In that case,
   169  	// DeposedKey in the plan is always states.NotDeposed, representing that
   170  	// the current object is being replaced with the deposed.
   171  	DeposedKey states.DeposedKey
   172  
   173  	// Provider is the address of the provider configuration that was used
   174  	// to plan this change, and thus the configuration that must also be
   175  	// used to apply it.
   176  	ProviderAddr addrs.AbsProviderConfig
   177  
   178  	// Change is an embedded description of the change.
   179  	Change
   180  
   181  	// ActionReason is an optional extra indication of why we chose the
   182  	// action recorded in Change.Action for this particular resource instance.
   183  	//
   184  	// This is an approximate mechanism only for the purpose of explaining the
   185  	// plan to end-users in the UI and is not to be used for any
   186  	// decision-making during the apply step; if apply behavior needs to vary
   187  	// depending on the "action reason" then the information for that decision
   188  	// must be recorded more precisely elsewhere for that purpose.
   189  	//
   190  	// Sometimes there might be more than one reason for choosing a particular
   191  	// action. In that case, it's up to the codepath making that decision to
   192  	// decide which value would provide the most relevant explanation to the
   193  	// end-user and return that. It's not a goal of this field to represent
   194  	// fine details about the planning process.
   195  	ActionReason ResourceInstanceChangeActionReason
   196  
   197  	// RequiredReplace is a set of paths that caused the change action to be
   198  	// Replace rather than Update. Always nil if the change action is not
   199  	// Replace.
   200  	//
   201  	// This is retained only for UI-plan-rendering purposes and so it does not
   202  	// currently survive a round-trip through a saved plan file.
   203  	RequiredReplace cty.PathSet
   204  
   205  	// Private allows a provider to stash any extra data that is opaque to
   206  	// Terraform that relates to this change. Terraform will save this
   207  	// byte-for-byte and return it to the provider in the apply call.
   208  	Private []byte
   209  }
   210  
   211  // Encode produces a variant of the reciever that has its change values
   212  // serialized so it can be written to a plan file. Pass the implied type of the
   213  // corresponding resource type schema for correct operation.
   214  func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) {
   215  	cs, err := rc.Change.Encode(ty)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	prevRunAddr := rc.PrevRunAddr
   220  	if prevRunAddr.Resource.Resource.Type == "" {
   221  		// Suggests an old caller that hasn't been properly updated to
   222  		// populate this yet.
   223  		prevRunAddr = rc.Addr
   224  	}
   225  	return &ResourceInstanceChangeSrc{
   226  		Addr:            rc.Addr,
   227  		PrevRunAddr:     prevRunAddr,
   228  		DeposedKey:      rc.DeposedKey,
   229  		ProviderAddr:    rc.ProviderAddr,
   230  		ChangeSrc:       *cs,
   231  		ActionReason:    rc.ActionReason,
   232  		RequiredReplace: rc.RequiredReplace,
   233  		Private:         rc.Private,
   234  	}, err
   235  }
   236  
   237  // Simplify will, where possible, produce a change with a simpler action than
   238  // the receiever given a flag indicating whether the caller is dealing with
   239  // a normal apply or a destroy. This flag deals with the fact that Terraform
   240  // Core uses a specialized graph node type for destroying; only that
   241  // specialized node should set "destroying" to true.
   242  //
   243  // The following table shows the simplification behavior:
   244  //
   245  //     Action    Destroying?   New Action
   246  //     --------+-------------+-----------
   247  //     Create    true          NoOp
   248  //     Delete    false         NoOp
   249  //     Replace   true          Delete
   250  //     Replace   false         Create
   251  //
   252  // For any combination not in the above table, the Simplify just returns the
   253  // receiver as-is.
   254  func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange {
   255  	if destroying {
   256  		switch rc.Action {
   257  		case Delete:
   258  			// We'll fall out and just return rc verbatim, then.
   259  		case CreateThenDelete, DeleteThenCreate:
   260  			return &ResourceInstanceChange{
   261  				Addr:         rc.Addr,
   262  				DeposedKey:   rc.DeposedKey,
   263  				Private:      rc.Private,
   264  				ProviderAddr: rc.ProviderAddr,
   265  				Change: Change{
   266  					Action: Delete,
   267  					Before: rc.Before,
   268  					After:  cty.NullVal(rc.Before.Type()),
   269  				},
   270  			}
   271  		default:
   272  			return &ResourceInstanceChange{
   273  				Addr:         rc.Addr,
   274  				DeposedKey:   rc.DeposedKey,
   275  				Private:      rc.Private,
   276  				ProviderAddr: rc.ProviderAddr,
   277  				Change: Change{
   278  					Action: NoOp,
   279  					Before: rc.Before,
   280  					After:  rc.Before,
   281  				},
   282  			}
   283  		}
   284  	} else {
   285  		switch rc.Action {
   286  		case Delete:
   287  			return &ResourceInstanceChange{
   288  				Addr:         rc.Addr,
   289  				DeposedKey:   rc.DeposedKey,
   290  				Private:      rc.Private,
   291  				ProviderAddr: rc.ProviderAddr,
   292  				Change: Change{
   293  					Action: NoOp,
   294  					Before: rc.Before,
   295  					After:  rc.Before,
   296  				},
   297  			}
   298  		case CreateThenDelete, DeleteThenCreate:
   299  			return &ResourceInstanceChange{
   300  				Addr:         rc.Addr,
   301  				DeposedKey:   rc.DeposedKey,
   302  				Private:      rc.Private,
   303  				ProviderAddr: rc.ProviderAddr,
   304  				Change: Change{
   305  					Action: Create,
   306  					Before: cty.NullVal(rc.After.Type()),
   307  					After:  rc.After,
   308  				},
   309  			}
   310  		}
   311  	}
   312  
   313  	// If we fall out here then our change is already simple enough.
   314  	return rc
   315  }
   316  
   317  // ResourceInstanceChangeActionReason allows for some extra user-facing
   318  // reasoning for why a particular change action was chosen for a particular
   319  // resource instance.
   320  //
   321  // This only represents sufficient detail to give a suitable explanation to
   322  // an end-user, and mustn't be used for any real decision-making during the
   323  // apply step.
   324  type ResourceInstanceChangeActionReason rune
   325  
   326  //go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceChangeActionReason changes.go
   327  
   328  const (
   329  	// In most cases there's no special reason for choosing a particular
   330  	// action, which is represented by ResourceInstanceChangeNoReason.
   331  	ResourceInstanceChangeNoReason ResourceInstanceChangeActionReason = 0
   332  
   333  	// ResourceInstanceReplaceBecauseTainted indicates that the resource
   334  	// instance must be replaced because its existing current object is
   335  	// marked as "tainted".
   336  	ResourceInstanceReplaceBecauseTainted ResourceInstanceChangeActionReason = 'T'
   337  
   338  	// ResourceInstanceReplaceByRequest indicates that the resource instance
   339  	// is planned to be replaced because a caller specifically asked for it
   340  	// to be using ReplaceAddrs. (On the command line, the -replace=...
   341  	// planning option.)
   342  	ResourceInstanceReplaceByRequest ResourceInstanceChangeActionReason = 'R'
   343  
   344  	// ResourceInstanceReplaceBecauseCannotUpdate indicates that the resource
   345  	// instance is planned to be replaced because the provider has indicated
   346  	// that a requested change cannot be applied as an update.
   347  	//
   348  	// In this case, the RequiredReplace field will typically be populated on
   349  	// the ResourceInstanceChange object to give information about specifically
   350  	// which arguments changed in a non-updatable way.
   351  	ResourceInstanceReplaceBecauseCannotUpdate ResourceInstanceChangeActionReason = 'F'
   352  
   353  	// ResourceInstanceDeleteBecauseNoResourceConfig indicates that the
   354  	// resource instance is planned to be deleted because there's no
   355  	// corresponding resource configuration block in the configuration.
   356  	ResourceInstanceDeleteBecauseNoResourceConfig ResourceInstanceChangeActionReason = 'N'
   357  
   358  	// ResourceInstanceDeleteBecauseWrongRepetition indicates that the
   359  	// resource instance is planned to be deleted because the instance key
   360  	// type isn't consistent with the repetition mode selected in the
   361  	// resource configuration.
   362  	ResourceInstanceDeleteBecauseWrongRepetition ResourceInstanceChangeActionReason = 'W'
   363  
   364  	// ResourceInstanceDeleteBecauseCountIndex indicates that the resource
   365  	// instance is planned to be deleted because its integer instance key
   366  	// is out of range for the current configured resource "count" value.
   367  	ResourceInstanceDeleteBecauseCountIndex ResourceInstanceChangeActionReason = 'C'
   368  
   369  	// ResourceInstanceDeleteBecauseEachKey indicates that the resource
   370  	// instance is planned to be deleted because its string instance key
   371  	// isn't one of the keys included in the current configured resource
   372  	// "for_each" value.
   373  	ResourceInstanceDeleteBecauseEachKey ResourceInstanceChangeActionReason = 'E'
   374  
   375  	// ResourceInstanceDeleteBecauseNoModule indicates that the resource
   376  	// instance is planned to be deleted because it belongs to a module
   377  	// instance that's no longer declared in the configuration.
   378  	//
   379  	// This is less specific than the reasons we return for the various ways
   380  	// a resource instance itself can be no longer declared, including both
   381  	// the total removal of a module block and changes to its count/for_each
   382  	// arguments. This difference in detail is out of pragmatism, because
   383  	// potentially multiple nested modules could all contribute conflicting
   384  	// specific reasons for a particular instance to no longer be declared.
   385  	ResourceInstanceDeleteBecauseNoModule ResourceInstanceChangeActionReason = 'M'
   386  )
   387  
   388  // OutputChange describes a change to an output value.
   389  type OutputChange struct {
   390  	// Addr is the absolute address of the output value that the change
   391  	// will apply to.
   392  	Addr addrs.AbsOutputValue
   393  
   394  	// Change is an embedded description of the change.
   395  	//
   396  	// For output value changes, the type constraint for the DynamicValue
   397  	// instances is always cty.DynamicPseudoType.
   398  	Change
   399  
   400  	// Sensitive, if true, indicates that either the old or new value in the
   401  	// change is sensitive and so a rendered version of the plan in the UI
   402  	// should elide the actual values while still indicating the action of the
   403  	// change.
   404  	Sensitive bool
   405  }
   406  
   407  // Encode produces a variant of the reciever that has its change values
   408  // serialized so it can be written to a plan file.
   409  func (oc *OutputChange) Encode() (*OutputChangeSrc, error) {
   410  	cs, err := oc.Change.Encode(cty.DynamicPseudoType)
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  	return &OutputChangeSrc{
   415  		Addr:      oc.Addr,
   416  		ChangeSrc: *cs,
   417  		Sensitive: oc.Sensitive,
   418  	}, err
   419  }
   420  
   421  // Change describes a single change with a given action.
   422  type Change struct {
   423  	// Action defines what kind of change is being made.
   424  	Action Action
   425  
   426  	// Interpretation of Before and After depend on Action:
   427  	//
   428  	//     NoOp     Before and After are the same, unchanged value
   429  	//     Create   Before is nil, and After is the expected value after create.
   430  	//     Read     Before is any prior value (nil if no prior), and After is the
   431  	//              value that was or will be read.
   432  	//     Update   Before is the value prior to update, and After is the expected
   433  	//              value after update.
   434  	//     Replace  As with Update.
   435  	//     Delete   Before is the value prior to delete, and After is always nil.
   436  	//
   437  	// Unknown values may appear anywhere within the Before and After values,
   438  	// either as the values themselves or as nested elements within known
   439  	// collections/structures.
   440  	Before, After cty.Value
   441  }
   442  
   443  // Encode produces a variant of the reciever that has its change values
   444  // serialized so it can be written to a plan file. Pass the type constraint
   445  // that the values are expected to conform to; to properly decode the values
   446  // later an identical type constraint must be provided at that time.
   447  //
   448  // Where a Change is embedded in some other struct, it's generally better
   449  // to call the corresponding Encode method of that struct rather than working
   450  // directly with its embedded Change.
   451  func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
   452  	// Storing unmarked values so that we can encode unmarked values
   453  	// and save the PathValueMarks for re-marking the values later
   454  	var beforeVM, afterVM []cty.PathValueMarks
   455  	unmarkedBefore := c.Before
   456  	unmarkedAfter := c.After
   457  
   458  	if c.Before.ContainsMarked() {
   459  		unmarkedBefore, beforeVM = c.Before.UnmarkDeepWithPaths()
   460  	}
   461  	beforeDV, err := NewDynamicValue(unmarkedBefore, ty)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  
   466  	if c.After.ContainsMarked() {
   467  		unmarkedAfter, afterVM = c.After.UnmarkDeepWithPaths()
   468  	}
   469  	afterDV, err := NewDynamicValue(unmarkedAfter, ty)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	return &ChangeSrc{
   475  		Action:         c.Action,
   476  		Before:         beforeDV,
   477  		After:          afterDV,
   478  		BeforeValMarks: beforeVM,
   479  		AfterValMarks:  afterVM,
   480  	}, nil
   481  }