github.com/pulumi/terraform@v1.4.0/pkg/plans/changes.go (about)

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