github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/resource/deploy/step.go (about)

     1  // Copyright 2016-2021, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package deploy
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"strings"
    21  
    22  	"github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers"
    23  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
    24  	"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
    25  	"github.com/pulumi/pulumi/sdk/v3/go/common/display"
    26  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
    27  	"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
    28  	"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
    29  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    30  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
    31  )
    32  
    33  // StepCompleteFunc is the type of functions returned from Step.Apply. These functions are to be called
    34  // when the engine has fully retired a step.
    35  type StepCompleteFunc func()
    36  
    37  // Step is a specification for a deployment operation.
    38  type Step interface {
    39  	// Apply applies or previews this step. It returns the status of the resource after the step application,
    40  	// a function to call to signal that this step has fully completed, and an error, if one occurred while applying
    41  	// the step.
    42  	//
    43  	// The returned StepCompleteFunc, if not nil, must be called after committing the results of this step into
    44  	// the state of the deployment.
    45  	Apply(preview bool) (resource.Status, StepCompleteFunc, error) // applies or previews this step.
    46  
    47  	Op() display.StepOp      // the operation performed by this step.
    48  	URN() resource.URN       // the resource URN (for before and after).
    49  	Type() tokens.Type       // the type affected by this step.
    50  	Provider() string        // the provider reference for this step.
    51  	Old() *resource.State    // the state of the resource before performing this step.
    52  	New() *resource.State    // the state of the resource after performing this step.
    53  	Res() *resource.State    // the latest state for the resource that is known (worst case, old).
    54  	Logical() bool           // true if this step represents a logical operation in the program.
    55  	Deployment() *Deployment // the owning deployment.
    56  }
    57  
    58  // SameStep is a mutating step that does nothing.
    59  type SameStep struct {
    60  	deployment *Deployment           // the current deployment.
    61  	reg        RegisterResourceEvent // the registration intent to convey a URN back to.
    62  	old        *resource.State       // the state of the resource before this step.
    63  	new        *resource.State       // the state of the resource after this step.
    64  
    65  	// If this is a same-step for a resource being created but which was not --target'ed by the user
    66  	// (and thus was skipped).
    67  	skippedCreate bool
    68  }
    69  
    70  var _ Step = (*SameStep)(nil)
    71  
    72  func NewSameStep(deployment *Deployment, reg RegisterResourceEvent, old, new *resource.State) Step {
    73  	contract.Assert(old != nil)
    74  	contract.Assert(old.URN != "")
    75  	contract.Assert(old.ID != "" || !old.Custom)
    76  	contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type))
    77  	contract.Assert(!old.Delete)
    78  	contract.Assert(new != nil)
    79  	contract.Assert(new.URN != "")
    80  	contract.Assert(new.ID == "")
    81  	contract.Assert(!new.Custom || new.Provider != "" || providers.IsProviderType(new.Type))
    82  	contract.Assert(!new.Delete)
    83  	return &SameStep{
    84  		deployment: deployment,
    85  		reg:        reg,
    86  		old:        old,
    87  		new:        new,
    88  	}
    89  }
    90  
    91  // NewSkippedCreateStep produces a SameStep for a resource that was created but not targeted
    92  // by the user (and thus was skipped). These act as no-op steps (hence 'same') since we are not
    93  // actually creating the resource, but ensure that we complete resource-registration and convey the
    94  // right information downstream. For example, we will not write these into the checkpoint file.
    95  func NewSkippedCreateStep(deployment *Deployment, reg RegisterResourceEvent, new *resource.State) Step {
    96  	contract.Assert(new != nil)
    97  	contract.Assert(new.URN != "")
    98  	contract.Assert(new.ID == "")
    99  	contract.Assert(!new.Custom || new.Provider != "" || providers.IsProviderType(new.Type))
   100  	contract.Assert(!new.Delete)
   101  
   102  	// Make the old state here a direct copy of the new state
   103  	old := *new
   104  	return &SameStep{
   105  		deployment:    deployment,
   106  		reg:           reg,
   107  		old:           &old,
   108  		new:           new,
   109  		skippedCreate: true,
   110  	}
   111  }
   112  
   113  func (s *SameStep) Op() display.StepOp      { return OpSame }
   114  func (s *SameStep) Deployment() *Deployment { return s.deployment }
   115  func (s *SameStep) Type() tokens.Type       { return s.new.Type }
   116  func (s *SameStep) Provider() string        { return s.new.Provider }
   117  func (s *SameStep) URN() resource.URN       { return s.new.URN }
   118  func (s *SameStep) Old() *resource.State    { return s.old }
   119  func (s *SameStep) New() *resource.State    { return s.new }
   120  func (s *SameStep) Res() *resource.State    { return s.new }
   121  func (s *SameStep) Logical() bool           { return true }
   122  
   123  func (s *SameStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   124  	// Retain the ID and outputs
   125  	s.new.ID = s.old.ID
   126  	s.new.Outputs = s.old.Outputs
   127  
   128  	// If the resource is a provider, ensure that it is present in the registry under the appropriate URNs.
   129  	if providers.IsProviderType(s.new.Type) {
   130  		ref, err := providers.NewReference(s.new.URN, s.new.ID)
   131  		if err != nil {
   132  			return resource.StatusOK, nil,
   133  				fmt.Errorf("bad provider reference '%v' for resource %v: %v", s.Provider(), s.URN(), err)
   134  		}
   135  		if s.Deployment() != nil {
   136  			s.Deployment().SameProvider(ref)
   137  		}
   138  	}
   139  
   140  	complete := func() { s.reg.Done(&RegisterResult{State: s.new}) }
   141  	return resource.StatusOK, complete, nil
   142  }
   143  
   144  func (s *SameStep) IsSkippedCreate() bool {
   145  	return s.skippedCreate
   146  }
   147  
   148  // CreateStep is a mutating step that creates an entirely new resource.
   149  type CreateStep struct {
   150  	deployment    *Deployment                    // the current deployment.
   151  	reg           RegisterResourceEvent          // the registration intent to convey a URN back to.
   152  	old           *resource.State                // the state of the existing resource (only for replacements).
   153  	new           *resource.State                // the state of the resource after this step.
   154  	keys          []resource.PropertyKey         // the keys causing replacement (only for replacements).
   155  	diffs         []resource.PropertyKey         // the keys causing a diff (only for replacements).
   156  	detailedDiff  map[string]plugin.PropertyDiff // the structured property diff (only for replacements).
   157  	replacing     bool                           // true if this is a create due to a replacement.
   158  	pendingDelete bool                           // true if this replacement should create a pending delete.
   159  }
   160  
   161  var _ Step = (*CreateStep)(nil)
   162  
   163  func NewCreateStep(deployment *Deployment, reg RegisterResourceEvent, new *resource.State) Step {
   164  	contract.Assert(reg != nil)
   165  	contract.Assert(new != nil)
   166  	contract.Assert(new.URN != "")
   167  	contract.Assert(new.ID == "")
   168  	contract.Assert(!new.Custom || new.Provider != "" || providers.IsProviderType(new.Type))
   169  	contract.Assert(!new.Delete)
   170  	contract.Assert(!new.External)
   171  	return &CreateStep{
   172  		deployment: deployment,
   173  		reg:        reg,
   174  		new:        new,
   175  	}
   176  }
   177  
   178  func NewCreateReplacementStep(deployment *Deployment, reg RegisterResourceEvent, old, new *resource.State,
   179  	keys, diffs []resource.PropertyKey, detailedDiff map[string]plugin.PropertyDiff, pendingDelete bool) Step {
   180  
   181  	contract.Assert(reg != nil)
   182  	contract.Assert(old != nil)
   183  	contract.Assert(old.URN != "")
   184  	contract.Assert(old.ID != "" || !old.Custom)
   185  	contract.Assert(!old.Delete)
   186  	contract.Assert(new != nil)
   187  	contract.Assert(new.URN != "")
   188  	contract.Assert(new.ID == "")
   189  	contract.Assert(!new.Custom || new.Provider != "" || providers.IsProviderType(new.Type))
   190  	contract.Assert(!new.Delete)
   191  	contract.Assert(!new.External)
   192  	return &CreateStep{
   193  		deployment:    deployment,
   194  		reg:           reg,
   195  		old:           old,
   196  		new:           new,
   197  		keys:          keys,
   198  		diffs:         diffs,
   199  		detailedDiff:  detailedDiff,
   200  		replacing:     true,
   201  		pendingDelete: pendingDelete,
   202  	}
   203  }
   204  
   205  func (s *CreateStep) Op() display.StepOp {
   206  	if s.replacing {
   207  		return OpCreateReplacement
   208  	}
   209  	return OpCreate
   210  }
   211  func (s *CreateStep) Deployment() *Deployment                      { return s.deployment }
   212  func (s *CreateStep) Type() tokens.Type                            { return s.new.Type }
   213  func (s *CreateStep) Provider() string                             { return s.new.Provider }
   214  func (s *CreateStep) URN() resource.URN                            { return s.new.URN }
   215  func (s *CreateStep) Old() *resource.State                         { return s.old }
   216  func (s *CreateStep) New() *resource.State                         { return s.new }
   217  func (s *CreateStep) Res() *resource.State                         { return s.new }
   218  func (s *CreateStep) Keys() []resource.PropertyKey                 { return s.keys }
   219  func (s *CreateStep) Diffs() []resource.PropertyKey                { return s.diffs }
   220  func (s *CreateStep) DetailedDiff() map[string]plugin.PropertyDiff { return s.detailedDiff }
   221  func (s *CreateStep) Logical() bool                                { return !s.replacing }
   222  
   223  func (s *CreateStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   224  	var resourceError error
   225  	resourceStatus := resource.StatusOK
   226  	if s.new.Custom {
   227  		// Invoke the Create RPC function for this provider:
   228  		prov, err := getProvider(s)
   229  		if err != nil {
   230  			return resource.StatusOK, nil, err
   231  		}
   232  
   233  		id, outs, rst, err := prov.Create(s.URN(), s.new.Inputs, s.new.CustomTimeouts.Create, s.deployment.preview)
   234  		if err != nil {
   235  			if rst != resource.StatusPartialFailure {
   236  				return rst, nil, err
   237  			}
   238  
   239  			resourceError = err
   240  			resourceStatus = rst
   241  
   242  			if initErr, isInitErr := err.(*plugin.InitError); isInitErr {
   243  				s.new.InitErrors = initErr.Reasons
   244  			}
   245  		}
   246  
   247  		if !preview && id == "" {
   248  			return resourceStatus, nil, fmt.Errorf("provider did not return an ID from Create")
   249  		}
   250  
   251  		// Copy any of the default and output properties on the live object state.
   252  		s.new.ID = id
   253  		s.new.Outputs = outs
   254  	}
   255  
   256  	// Mark the old resource as pending deletion if necessary.
   257  	if s.replacing && s.pendingDelete {
   258  		s.old.Delete = true
   259  	}
   260  
   261  	complete := func() { s.reg.Done(&RegisterResult{State: s.new}) }
   262  	if resourceError == nil {
   263  		return resourceStatus, complete, nil
   264  	}
   265  	return resourceStatus, complete, resourceError
   266  }
   267  
   268  // DeleteStep is a mutating step that deletes an existing resource. If `old` is marked "External",
   269  // DeleteStep is a no-op.
   270  type DeleteStep struct {
   271  	deployment     *Deployment           // the current deployment.
   272  	old            *resource.State       // the state of the existing resource.
   273  	replacing      bool                  // true if part of a replacement.
   274  	otherDeletions map[resource.URN]bool // other resources that are planned to delete
   275  }
   276  
   277  var _ Step = (*DeleteStep)(nil)
   278  
   279  func NewDeleteStep(deployment *Deployment, otherDeletions map[resource.URN]bool, old *resource.State) Step {
   280  	contract.Assert(old != nil)
   281  	contract.Assert(old.URN != "")
   282  	contract.Assert(old.ID != "" || !old.Custom)
   283  	contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type))
   284  	contract.Assert(otherDeletions != nil)
   285  	return &DeleteStep{
   286  		deployment:     deployment,
   287  		old:            old,
   288  		otherDeletions: otherDeletions,
   289  	}
   290  }
   291  
   292  func NewDeleteReplacementStep(
   293  	deployment *Deployment,
   294  	otherDeletions map[resource.URN]bool,
   295  	old *resource.State,
   296  	pendingReplace bool,
   297  ) Step {
   298  	contract.Assert(old != nil)
   299  	contract.Assert(old.URN != "")
   300  	contract.Assert(old.ID != "" || !old.Custom)
   301  	contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type))
   302  	contract.Assert(otherDeletions != nil)
   303  
   304  	// There are two cases in which we create a delete-replacment step:
   305  	//
   306  	//   1. When creating the delete steps that occur due to a delete-before-replace
   307  	//   2. When creating the delete step that occurs due to a delete-after-replace
   308  	//
   309  	// In the former case, the persistence layer may require that the resource remain in the
   310  	// checkpoint file for purposes of checkpoint integrity. We communicate this case by means
   311  	// of the `PendingReplacement` field on `resource.State`, which we set here.
   312  	//
   313  	// In the latter case, the resource must be deleted, but the deletion may not occur if an earlier step fails.
   314  	// The engine requires that the fact that the old resource must be deleted is persisted in the checkpoint so
   315  	// that it can issue a deletion of this resource on the next update to this stack.
   316  	contract.Assert(pendingReplace != old.Delete)
   317  	old.PendingReplacement = pendingReplace
   318  	return &DeleteStep{
   319  		deployment:     deployment,
   320  		otherDeletions: otherDeletions,
   321  		old:            old,
   322  		replacing:      true,
   323  	}
   324  }
   325  
   326  func (s *DeleteStep) Op() display.StepOp {
   327  	if s.old.External {
   328  		if s.replacing {
   329  			return OpDiscardReplaced
   330  		}
   331  		return OpReadDiscard
   332  	}
   333  
   334  	if s.replacing {
   335  		return OpDeleteReplaced
   336  	}
   337  	return OpDelete
   338  }
   339  func (s *DeleteStep) Deployment() *Deployment { return s.deployment }
   340  func (s *DeleteStep) Type() tokens.Type       { return s.old.Type }
   341  func (s *DeleteStep) Provider() string        { return s.old.Provider }
   342  func (s *DeleteStep) URN() resource.URN       { return s.old.URN }
   343  func (s *DeleteStep) Old() *resource.State    { return s.old }
   344  func (s *DeleteStep) New() *resource.State    { return nil }
   345  func (s *DeleteStep) Res() *resource.State    { return s.old }
   346  func (s *DeleteStep) Logical() bool           { return !s.replacing }
   347  
   348  func isDeletedWith(with resource.URN, otherDeletions map[resource.URN]bool) bool {
   349  	if with == "" {
   350  		return false
   351  	}
   352  	r, ok := otherDeletions[with]
   353  	if !ok {
   354  		return false
   355  	}
   356  	return r
   357  }
   358  
   359  func (s *DeleteStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   360  	// Refuse to delete protected resources (unless we're replacing them in
   361  	// which case we will of checked protect elsewhere)
   362  	if !s.replacing && s.old.Protect {
   363  		return resource.StatusOK, nil, fmt.Errorf("unable to delete resource %q\n"+
   364  			"as it is currently marked for protection. To unprotect the resource, "+
   365  			"either remove the `protect` flag from the resource in your Pulumi "+
   366  			"program and run `pulumi up` or use the command:\n"+
   367  			"`pulumi state unprotect '%s'`", s.old.URN, s.old.URN)
   368  	}
   369  
   370  	if preview {
   371  		// Do nothing in preview
   372  	} else if s.old.External {
   373  		// Deleting an External resource is a no-op, since Pulumi does not own the lifecycle.
   374  	} else if s.old.RetainOnDelete {
   375  		// Deleting a "drop on delete" is a no-op as the user has explicitly asked us to not delete the resource.
   376  	} else if isDeletedWith(s.old.DeletedWith, s.otherDeletions) {
   377  		// No need to delete this resource since this resource will be deleted by the another deletion
   378  	} else if s.old.Custom {
   379  		// Not preview and not external and not Drop and is custom, do the actual delete
   380  
   381  		// Invoke the Delete RPC function for this provider:
   382  		prov, err := getProvider(s)
   383  		if err != nil {
   384  			return resource.StatusOK, nil, err
   385  		}
   386  
   387  		if rst, err := prov.Delete(s.URN(), s.old.ID, s.old.Outputs, s.old.CustomTimeouts.Delete); err != nil {
   388  			return rst, nil, err
   389  		}
   390  	}
   391  
   392  	return resource.StatusOK, func() {}, nil
   393  }
   394  
   395  type RemovePendingReplaceStep struct {
   396  	deployment *Deployment     // the current deployment.
   397  	old        *resource.State // the state of the existing resource.
   398  }
   399  
   400  func NewRemovePendingReplaceStep(deployment *Deployment, old *resource.State) Step {
   401  	contract.Assert(old != nil)
   402  	contract.Assert(old.PendingReplacement)
   403  	return &RemovePendingReplaceStep{
   404  		deployment: deployment,
   405  		old:        old,
   406  	}
   407  }
   408  
   409  func (s *RemovePendingReplaceStep) Op() display.StepOp {
   410  	return OpRemovePendingReplace
   411  }
   412  func (s *RemovePendingReplaceStep) Deployment() *Deployment { return s.deployment }
   413  func (s *RemovePendingReplaceStep) Type() tokens.Type       { return s.old.Type }
   414  func (s *RemovePendingReplaceStep) Provider() string        { return s.old.Provider }
   415  func (s *RemovePendingReplaceStep) URN() resource.URN       { return s.old.URN }
   416  func (s *RemovePendingReplaceStep) Old() *resource.State    { return s.old }
   417  func (s *RemovePendingReplaceStep) New() *resource.State    { return nil }
   418  func (s *RemovePendingReplaceStep) Res() *resource.State    { return s.old }
   419  func (s *RemovePendingReplaceStep) Logical() bool           { return false }
   420  
   421  func (s *RemovePendingReplaceStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   422  	return resource.StatusOK, nil, nil
   423  }
   424  
   425  // UpdateStep is a mutating step that updates an existing resource's state.
   426  type UpdateStep struct {
   427  	deployment    *Deployment                    // the current deployment.
   428  	reg           RegisterResourceEvent          // the registration intent to convey a URN back to.
   429  	old           *resource.State                // the state of the existing resource.
   430  	new           *resource.State                // the newly computed state of the resource after updating.
   431  	stables       []resource.PropertyKey         // an optional list of properties that won't change during this update.
   432  	diffs         []resource.PropertyKey         // the keys causing a diff.
   433  	detailedDiff  map[string]plugin.PropertyDiff // the structured diff.
   434  	ignoreChanges []string                       // a list of property paths to ignore when updating.
   435  }
   436  
   437  var _ Step = (*UpdateStep)(nil)
   438  
   439  func NewUpdateStep(deployment *Deployment, reg RegisterResourceEvent, old, new *resource.State,
   440  	stables, diffs []resource.PropertyKey, detailedDiff map[string]plugin.PropertyDiff,
   441  
   442  	ignoreChanges []string) Step {
   443  	contract.Assert(old != nil)
   444  	contract.Assert(old.URN != "")
   445  	contract.Assert(old.ID != "" || !old.Custom)
   446  	contract.Assert(!old.Custom || old.Provider != "" || providers.IsProviderType(old.Type))
   447  	contract.Assert(!old.Delete)
   448  	contract.Assert(new != nil)
   449  	contract.Assert(new.URN != "")
   450  	contract.Assert(new.ID == "")
   451  	contract.Assert(!new.Custom || new.Provider != "" || providers.IsProviderType(new.Type))
   452  	contract.Assert(!new.Delete)
   453  	contract.Assert(!new.External)
   454  	contract.Assert(!old.External)
   455  	return &UpdateStep{
   456  		deployment:    deployment,
   457  		reg:           reg,
   458  		old:           old,
   459  		new:           new,
   460  		stables:       stables,
   461  		diffs:         diffs,
   462  		detailedDiff:  detailedDiff,
   463  		ignoreChanges: ignoreChanges,
   464  	}
   465  }
   466  
   467  func (s *UpdateStep) Op() display.StepOp                           { return OpUpdate }
   468  func (s *UpdateStep) Deployment() *Deployment                      { return s.deployment }
   469  func (s *UpdateStep) Type() tokens.Type                            { return s.new.Type }
   470  func (s *UpdateStep) Provider() string                             { return s.new.Provider }
   471  func (s *UpdateStep) URN() resource.URN                            { return s.new.URN }
   472  func (s *UpdateStep) Old() *resource.State                         { return s.old }
   473  func (s *UpdateStep) New() *resource.State                         { return s.new }
   474  func (s *UpdateStep) Res() *resource.State                         { return s.new }
   475  func (s *UpdateStep) Logical() bool                                { return true }
   476  func (s *UpdateStep) Diffs() []resource.PropertyKey                { return s.diffs }
   477  func (s *UpdateStep) DetailedDiff() map[string]plugin.PropertyDiff { return s.detailedDiff }
   478  
   479  func (s *UpdateStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   480  	// Always propagate the ID, even in previews and refreshes.
   481  	s.new.ID = s.old.ID
   482  
   483  	var resourceError error
   484  	resourceStatus := resource.StatusOK
   485  	if s.new.Custom {
   486  		// Invoke the Update RPC function for this provider:
   487  		prov, err := getProvider(s)
   488  		if err != nil {
   489  			return resource.StatusOK, nil, err
   490  		}
   491  
   492  		// Update to the combination of the old "all" state, but overwritten with new inputs.
   493  		outs, rst, upderr := prov.Update(s.URN(), s.old.ID, s.old.Outputs, s.new.Inputs,
   494  			s.new.CustomTimeouts.Update, s.ignoreChanges, s.deployment.preview)
   495  		if upderr != nil {
   496  			if rst != resource.StatusPartialFailure {
   497  				return rst, nil, upderr
   498  			}
   499  
   500  			resourceError = upderr
   501  			resourceStatus = rst
   502  
   503  			if initErr, isInitErr := upderr.(*plugin.InitError); isInitErr {
   504  				s.new.InitErrors = initErr.Reasons
   505  			}
   506  		}
   507  
   508  		// Now copy any output state back in case the update triggered cascading updates to other properties.
   509  		s.new.Outputs = outs
   510  	}
   511  
   512  	// Finally, mark this operation as complete.
   513  	complete := func() { s.reg.Done(&RegisterResult{State: s.new}) }
   514  	if resourceError == nil {
   515  		return resourceStatus, complete, nil
   516  	}
   517  	return resourceStatus, complete, resourceError
   518  }
   519  
   520  // ReplaceStep is a logical step indicating a resource will be replaced.  This is comprised of three physical steps:
   521  // a creation of the new resource, any number of intervening updates of dependents to the new resource, and then
   522  // a deletion of the now-replaced old resource.  This logical step is primarily here for tools and visualization.
   523  type ReplaceStep struct {
   524  	deployment    *Deployment                    // the current deployment.
   525  	old           *resource.State                // the state of the existing resource.
   526  	new           *resource.State                // the new state snapshot.
   527  	keys          []resource.PropertyKey         // the keys causing replacement.
   528  	diffs         []resource.PropertyKey         // the keys causing a diff.
   529  	detailedDiff  map[string]plugin.PropertyDiff // the structured property diff.
   530  	pendingDelete bool                           // true if a pending deletion should happen.
   531  }
   532  
   533  var _ Step = (*ReplaceStep)(nil)
   534  
   535  func NewReplaceStep(deployment *Deployment, old, new *resource.State, keys, diffs []resource.PropertyKey,
   536  	detailedDiff map[string]plugin.PropertyDiff, pendingDelete bool) Step {
   537  
   538  	contract.Assert(old != nil)
   539  	contract.Assert(old.URN != "")
   540  	contract.Assert(old.ID != "" || !old.Custom)
   541  	contract.Assert(!old.Delete)
   542  	contract.Assert(new != nil)
   543  	contract.Assert(new.URN != "")
   544  	// contract.Assert(new.ID == "")
   545  	contract.Assert(!new.Delete)
   546  	return &ReplaceStep{
   547  		deployment:    deployment,
   548  		old:           old,
   549  		new:           new,
   550  		keys:          keys,
   551  		diffs:         diffs,
   552  		detailedDiff:  detailedDiff,
   553  		pendingDelete: pendingDelete,
   554  	}
   555  }
   556  
   557  func (s *ReplaceStep) Op() display.StepOp                           { return OpReplace }
   558  func (s *ReplaceStep) Deployment() *Deployment                      { return s.deployment }
   559  func (s *ReplaceStep) Type() tokens.Type                            { return s.new.Type }
   560  func (s *ReplaceStep) Provider() string                             { return s.new.Provider }
   561  func (s *ReplaceStep) URN() resource.URN                            { return s.new.URN }
   562  func (s *ReplaceStep) Old() *resource.State                         { return s.old }
   563  func (s *ReplaceStep) New() *resource.State                         { return s.new }
   564  func (s *ReplaceStep) Res() *resource.State                         { return s.new }
   565  func (s *ReplaceStep) Keys() []resource.PropertyKey                 { return s.keys }
   566  func (s *ReplaceStep) Diffs() []resource.PropertyKey                { return s.diffs }
   567  func (s *ReplaceStep) DetailedDiff() map[string]plugin.PropertyDiff { return s.detailedDiff }
   568  func (s *ReplaceStep) Logical() bool                                { return true }
   569  
   570  func (s *ReplaceStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   571  	// If this is a pending delete, we should have marked the old resource for deletion in the CreateReplacement step.
   572  	contract.Assert(!s.pendingDelete || s.old.Delete)
   573  	return resource.StatusOK, func() {}, nil
   574  }
   575  
   576  // ReadStep is a step indicating that an existing resources will be "read" and projected into the Pulumi object
   577  // model. Resources that are read are marked with the "External" bit which indicates to the engine that it does
   578  // not own this resource's lifeycle.
   579  //
   580  // A resource with a given URN can transition freely between an "external" state and a non-external state. If
   581  // a URN that was previously marked "External" (i.e. was the target of a ReadStep in a previous deployment) is the
   582  // target of a RegisterResource in the next deployment, a CreateReplacement step will be issued to indicate the
   583  // transition from external to owned. If a URN that was previously not marked "External" is the target of a
   584  // ReadResource in the next deployment, a ReadReplacement step will be issued to indicate the transition from owned to
   585  // external.
   586  type ReadStep struct {
   587  	deployment *Deployment       // the deployment that produced this read
   588  	event      ReadResourceEvent // the event that should be signaled upon completion
   589  	old        *resource.State   // the old resource state, if one exists for this urn
   590  	new        *resource.State   // the new resource state, to be used to query the provider
   591  	replacing  bool              // whether or not the new resource is replacing the old resource
   592  }
   593  
   594  // NewReadStep creates a new Read step.
   595  func NewReadStep(deployment *Deployment, event ReadResourceEvent, old, new *resource.State) Step {
   596  	contract.Assert(new != nil)
   597  	contract.Assertf(new.URN != "", "Read URN was empty")
   598  	contract.Assertf(new.ID != "", "Read ID was empty")
   599  	contract.Assertf(new.External, "target of Read step must be marked External")
   600  	contract.Assertf(new.Custom, "target of Read step must be Custom")
   601  
   602  	// If Old was given, it's either an external resource or its ID is equal to the
   603  	// ID that we are preparing to read.
   604  	if old != nil {
   605  		contract.Assert(old.ID == new.ID || old.External)
   606  	}
   607  
   608  	return &ReadStep{
   609  		deployment: deployment,
   610  		event:      event,
   611  		old:        old,
   612  		new:        new,
   613  		replacing:  false,
   614  	}
   615  }
   616  
   617  // NewReadReplacementStep creates a new Read step with the `replacing` flag set. When executed,
   618  // it will pend deletion of the "old" resource, which must not be an external resource.
   619  func NewReadReplacementStep(deployment *Deployment, event ReadResourceEvent, old, new *resource.State) Step {
   620  	contract.Assert(new != nil)
   621  	contract.Assertf(new.URN != "", "Read URN was empty")
   622  	contract.Assertf(new.ID != "", "Read ID was empty")
   623  	contract.Assertf(new.External, "target of ReadReplacement step must be marked External")
   624  	contract.Assertf(new.Custom, "target of ReadReplacement step must be Custom")
   625  	contract.Assert(old != nil)
   626  	contract.Assertf(!old.External, "old target of ReadReplacement step must not be External")
   627  	return &ReadStep{
   628  		deployment: deployment,
   629  		event:      event,
   630  		old:        old,
   631  		new:        new,
   632  		replacing:  true,
   633  	}
   634  }
   635  
   636  func (s *ReadStep) Op() display.StepOp {
   637  	if s.replacing {
   638  		return OpReadReplacement
   639  	}
   640  
   641  	return OpRead
   642  }
   643  
   644  func (s *ReadStep) Deployment() *Deployment { return s.deployment }
   645  func (s *ReadStep) Type() tokens.Type       { return s.new.Type }
   646  func (s *ReadStep) Provider() string        { return s.new.Provider }
   647  func (s *ReadStep) URN() resource.URN       { return s.new.URN }
   648  func (s *ReadStep) Old() *resource.State    { return s.old }
   649  func (s *ReadStep) New() *resource.State    { return s.new }
   650  func (s *ReadStep) Res() *resource.State    { return s.new }
   651  func (s *ReadStep) Logical() bool           { return !s.replacing }
   652  
   653  func (s *ReadStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   654  	urn := s.new.URN
   655  	id := s.new.ID
   656  
   657  	var resourceError error
   658  	resourceStatus := resource.StatusOK
   659  	// Unlike most steps, Read steps run during previews. The only time
   660  	// we can't run is if the ID we are given is unknown.
   661  	if id == plugin.UnknownStringValue {
   662  		s.new.Outputs = resource.PropertyMap{}
   663  	} else {
   664  		prov, err := getProvider(s)
   665  		if err != nil {
   666  			return resource.StatusOK, nil, err
   667  		}
   668  
   669  		result, rst, err := prov.Read(urn, id, nil, s.new.Inputs)
   670  		if err != nil {
   671  			if rst != resource.StatusPartialFailure {
   672  				return rst, nil, err
   673  			}
   674  
   675  			resourceError = err
   676  			resourceStatus = rst
   677  
   678  			if initErr, isInitErr := err.(*plugin.InitError); isInitErr {
   679  				s.new.InitErrors = initErr.Reasons
   680  			}
   681  		}
   682  
   683  		// If there is no such resource, return an error indicating as such.
   684  		if result.Outputs == nil {
   685  			return resource.StatusOK, nil, fmt.Errorf("resource '%s' does not exist", id)
   686  		}
   687  		s.new.Outputs = result.Outputs
   688  
   689  		if result.ID != "" {
   690  			s.new.ID = result.ID
   691  		}
   692  	}
   693  
   694  	// If we were asked to replace an existing, non-External resource, pend the
   695  	// deletion here.
   696  	if s.replacing {
   697  		s.old.Delete = true
   698  	}
   699  
   700  	complete := func() { s.event.Done(&ReadResult{State: s.new}) }
   701  	if resourceError == nil {
   702  		return resourceStatus, complete, nil
   703  	}
   704  	return resourceStatus, complete, resourceError
   705  }
   706  
   707  // RefreshStep is a step used to track the progress of a refresh operation. A refresh operation updates the an existing
   708  // resource by reading its current state from its provider plugin. These steps are not issued by the step generator;
   709  // instead, they are issued by the deployment executor as the optional first step in deployment execution.
   710  type RefreshStep struct {
   711  	deployment *Deployment     // the deployment that produced this refresh
   712  	old        *resource.State // the old resource state, if one exists for this urn
   713  	new        *resource.State // the new resource state, to be used to query the provider
   714  	done       chan<- bool     // the channel to use to signal completion, if any
   715  }
   716  
   717  // NewRefreshStep creates a new Refresh step.
   718  func NewRefreshStep(deployment *Deployment, old *resource.State, done chan<- bool) Step {
   719  	contract.Assert(old != nil)
   720  
   721  	// NOTE: we set the new state to the old state by default so that we don't interpret step failures as deletes.
   722  	return &RefreshStep{
   723  		deployment: deployment,
   724  		old:        old,
   725  		new:        old,
   726  		done:       done,
   727  	}
   728  }
   729  
   730  func (s *RefreshStep) Op() display.StepOp      { return OpRefresh }
   731  func (s *RefreshStep) Deployment() *Deployment { return s.deployment }
   732  func (s *RefreshStep) Type() tokens.Type       { return s.old.Type }
   733  func (s *RefreshStep) Provider() string        { return s.old.Provider }
   734  func (s *RefreshStep) URN() resource.URN       { return s.old.URN }
   735  func (s *RefreshStep) Old() *resource.State    { return s.old }
   736  func (s *RefreshStep) New() *resource.State    { return s.new }
   737  func (s *RefreshStep) Res() *resource.State    { return s.old }
   738  func (s *RefreshStep) Logical() bool           { return false }
   739  
   740  // ResultOp returns the operation that corresponds to the change to this resource after reading its current state, if
   741  // any.
   742  func (s *RefreshStep) ResultOp() display.StepOp {
   743  	if s.new == nil {
   744  		return OpDelete
   745  	}
   746  	if s.new == s.old || s.old.Outputs.Diff(s.new.Outputs) == nil {
   747  		return OpSame
   748  	}
   749  	return OpUpdate
   750  }
   751  
   752  func (s *RefreshStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   753  	var complete func()
   754  	if s.done != nil {
   755  		complete = func() { close(s.done) }
   756  	}
   757  
   758  	resourceID := s.old.ID
   759  
   760  	// Component, provider, and pending-replace resources never change with a refresh; just return the current state.
   761  	if !s.old.Custom || providers.IsProviderType(s.old.Type) || s.old.PendingReplacement {
   762  		return resource.StatusOK, complete, nil
   763  	}
   764  
   765  	// For a custom resource, fetch the resource's provider and read the resource's current state.
   766  	prov, err := getProvider(s)
   767  	if err != nil {
   768  		return resource.StatusOK, nil, err
   769  	}
   770  
   771  	var initErrors []string
   772  	refreshed, rst, err := prov.Read(s.old.URN, resourceID, s.old.Inputs, s.old.Outputs)
   773  	if err != nil {
   774  		if rst != resource.StatusPartialFailure {
   775  			return rst, nil, err
   776  		}
   777  		if initErr, isInitErr := err.(*plugin.InitError); isInitErr {
   778  			initErrors = initErr.Reasons
   779  
   780  			// Partial failure SHOULD NOT cause refresh to fail. Instead:
   781  			//
   782  			// 1. Warn instead that during refresh we noticed the resource has become unhealthy.
   783  			// 2. Make sure the initialization errors are persisted in the state, so that the next
   784  			//    `pulumi up` will surface them to the user.
   785  			err = nil
   786  			msg := fmt.Sprintf("Refreshed resource is in an unhealthy state:\n* %s", strings.Join(initErrors, "\n* "))
   787  			s.Deployment().Diag().Warningf(diag.RawMessage(s.URN(), msg))
   788  		}
   789  	}
   790  	outputs := refreshed.Outputs
   791  
   792  	// If the provider specified new inputs for this resource, pick them up now. Otherwise, retain the current inputs.
   793  	inputs := s.old.Inputs
   794  	if refreshed.Inputs != nil {
   795  		inputs = refreshed.Inputs
   796  	}
   797  
   798  	if outputs != nil {
   799  		// There is a chance that the ID has changed. We want to allow this change to happen
   800  		// it will have changed already in the outputs, but we need to persist this change
   801  		// at a state level because the Id
   802  		if refreshed.ID != "" && refreshed.ID != resourceID {
   803  			logging.V(7).Infof("Refreshing ID; oldId=%s, newId=%s", resourceID, refreshed.ID)
   804  			resourceID = refreshed.ID
   805  		}
   806  
   807  		s.new = resource.NewState(s.old.Type, s.old.URN, s.old.Custom, s.old.Delete, resourceID, inputs, outputs,
   808  			s.old.Parent, s.old.Protect, s.old.External, s.old.Dependencies, initErrors, s.old.Provider,
   809  			s.old.PropertyDependencies, s.old.PendingReplacement, s.old.AdditionalSecretOutputs, s.old.Aliases,
   810  			&s.old.CustomTimeouts, s.old.ImportID, s.old.RetainOnDelete, s.old.DeletedWith)
   811  	} else {
   812  		s.new = nil
   813  	}
   814  
   815  	return rst, complete, err
   816  }
   817  
   818  type ImportStep struct {
   819  	deployment    *Deployment                    // the current deployment.
   820  	reg           RegisterResourceEvent          // the registration intent to convey a URN back to.
   821  	original      *resource.State                // the original resource, if this is an import-replace.
   822  	old           *resource.State                // the state of the resource fetched from the provider.
   823  	new           *resource.State                // the newly computed state of the resource after importing.
   824  	replacing     bool                           // true if we are replacing a Pulumi-managed resource.
   825  	planned       bool                           // true if this import is from an import deployment.
   826  	diffs         []resource.PropertyKey         // any keys that differed between the user's program and the actual state.
   827  	detailedDiff  map[string]plugin.PropertyDiff // the structured property diff.
   828  	ignoreChanges []string                       // a list of property paths to ignore when updating.
   829  	randomSeed    []byte                         // the random seed to use for Check.
   830  }
   831  
   832  func NewImportStep(deployment *Deployment, reg RegisterResourceEvent, new *resource.State,
   833  	ignoreChanges []string, randomSeed []byte) Step {
   834  
   835  	contract.Assert(new != nil)
   836  	contract.Assert(new.URN != "")
   837  	contract.Assert(new.ID != "")
   838  	contract.Assert(new.Custom)
   839  	contract.Assert(!new.Delete)
   840  	contract.Assert(!new.External)
   841  	contract.Assert(randomSeed != nil)
   842  
   843  	return &ImportStep{
   844  		deployment:    deployment,
   845  		reg:           reg,
   846  		new:           new,
   847  		ignoreChanges: ignoreChanges,
   848  		randomSeed:    randomSeed,
   849  	}
   850  }
   851  
   852  func NewImportReplacementStep(deployment *Deployment, reg RegisterResourceEvent, original, new *resource.State,
   853  	ignoreChanges []string, randomSeed []byte) Step {
   854  
   855  	contract.Assert(original != nil)
   856  	contract.Assert(new != nil)
   857  	contract.Assert(new.URN != "")
   858  	contract.Assert(new.ID != "")
   859  	contract.Assert(new.Custom)
   860  	contract.Assert(!new.Delete)
   861  	contract.Assert(!new.External)
   862  	contract.Assert(randomSeed != nil)
   863  
   864  	return &ImportStep{
   865  		deployment:    deployment,
   866  		reg:           reg,
   867  		original:      original,
   868  		new:           new,
   869  		replacing:     true,
   870  		ignoreChanges: ignoreChanges,
   871  		randomSeed:    randomSeed,
   872  	}
   873  }
   874  
   875  func newImportDeploymentStep(deployment *Deployment, new *resource.State, randomSeed []byte) Step {
   876  	contract.Assert(new != nil)
   877  	contract.Assert(new.URN != "")
   878  	contract.Assert(new.ID != "")
   879  	contract.Assert(new.Custom)
   880  	contract.Assert(!new.Delete)
   881  	contract.Assert(!new.External)
   882  	contract.Assert(randomSeed != nil)
   883  
   884  	return &ImportStep{
   885  		deployment: deployment,
   886  		reg:        noopEvent(0),
   887  		new:        new,
   888  		planned:    true,
   889  		randomSeed: randomSeed,
   890  	}
   891  }
   892  
   893  func (s *ImportStep) Op() display.StepOp {
   894  	if s.replacing {
   895  		return OpImportReplacement
   896  	}
   897  	return OpImport
   898  }
   899  
   900  func (s *ImportStep) Deployment() *Deployment                      { return s.deployment }
   901  func (s *ImportStep) Type() tokens.Type                            { return s.new.Type }
   902  func (s *ImportStep) Provider() string                             { return s.new.Provider }
   903  func (s *ImportStep) URN() resource.URN                            { return s.new.URN }
   904  func (s *ImportStep) Old() *resource.State                         { return s.old }
   905  func (s *ImportStep) New() *resource.State                         { return s.new }
   906  func (s *ImportStep) Res() *resource.State                         { return s.new }
   907  func (s *ImportStep) Logical() bool                                { return !s.replacing }
   908  func (s *ImportStep) Diffs() []resource.PropertyKey                { return s.diffs }
   909  func (s *ImportStep) DetailedDiff() map[string]plugin.PropertyDiff { return s.detailedDiff }
   910  
   911  func (s *ImportStep) Apply(preview bool) (resource.Status, StepCompleteFunc, error) {
   912  	complete := func() { s.reg.Done(&RegisterResult{State: s.new}) }
   913  
   914  	// If this is a planned import, ensure that the resource does not exist in the old state file.
   915  	if s.planned {
   916  		if _, ok := s.deployment.olds[s.new.URN]; ok {
   917  			return resource.StatusOK, nil, fmt.Errorf("resource '%v' already exists", s.new.URN)
   918  		}
   919  		if s.new.Parent.Type() != resource.RootStackType {
   920  			if _, ok := s.deployment.olds[s.new.Parent]; !ok {
   921  				return resource.StatusOK, nil, fmt.Errorf("unknown parent '%v' for resource '%v'",
   922  					s.new.Parent, s.new.URN)
   923  			}
   924  		}
   925  	}
   926  
   927  	// Read the current state of the resource to import. If the provider does not hand us back any inputs for the
   928  	// resource, it probably needs to be updated. If the resource does not exist at all, fail the import.
   929  	prov, err := getProvider(s)
   930  	if err != nil {
   931  		return resource.StatusOK, nil, err
   932  	}
   933  	read, rst, err := prov.Read(s.new.URN, s.new.ID, nil, nil)
   934  	if err != nil {
   935  		if initErr, isInitErr := err.(*plugin.InitError); isInitErr {
   936  			s.new.InitErrors = initErr.Reasons
   937  		} else {
   938  			return rst, nil, err
   939  		}
   940  	}
   941  	if read.Outputs == nil {
   942  		return rst, nil, fmt.Errorf("resource '%v' does not exist", s.new.ID)
   943  	}
   944  	if read.Inputs == nil {
   945  		return resource.StatusOK, nil,
   946  			fmt.Errorf("provider does not support importing resources; please try updating the '%v' plugin",
   947  				s.new.URN.Type().Package())
   948  	}
   949  	if read.ID != "" {
   950  		s.new.ID = read.ID
   951  	}
   952  	s.new.Outputs = read.Outputs
   953  
   954  	// Magic up an old state so the frontend can display a proper diff. This state is the output of the just-executed
   955  	// `Read` combined with the resource identity and metadata from the desired state. This ensures that the only
   956  	// differences between the old and new states are between the inputs and outputs.
   957  	s.old = resource.NewState(s.new.Type, s.new.URN, s.new.Custom, false, s.new.ID, read.Inputs, read.Outputs,
   958  		s.new.Parent, s.new.Protect, false, s.new.Dependencies, s.new.InitErrors, s.new.Provider,
   959  		s.new.PropertyDependencies, false, nil, nil, &s.new.CustomTimeouts, s.new.ImportID, s.new.RetainOnDelete,
   960  		s.new.DeletedWith)
   961  
   962  	// If this step came from an import deployment, we need to fetch any required inputs from the state.
   963  	if s.planned {
   964  		contract.Assert(len(s.new.Inputs) == 0)
   965  
   966  		// Get the import object and see if it had properties set
   967  		var inputProperties []string
   968  		for _, imp := range s.deployment.imports {
   969  			if imp.ID == s.old.ID {
   970  				inputProperties = imp.Properties
   971  				break
   972  			}
   973  		}
   974  
   975  		if len(inputProperties) == 0 {
   976  			logging.V(9).Infof("Importing %v with all properties", s.URN())
   977  			s.new.Inputs = s.old.Inputs.Copy()
   978  		} else {
   979  			logging.V(9).Infof("Importing %v with supplied properties: %v", s.URN(), inputProperties)
   980  			for _, p := range inputProperties {
   981  				k := resource.PropertyKey(p)
   982  				if value, has := s.old.Inputs[k]; has {
   983  					s.new.Inputs[k] = value
   984  				}
   985  			}
   986  		}
   987  
   988  		// Check the provider inputs for consistency. If the inputs fail validation, the import will still succeed, but
   989  		// we will display the validation failures and a message informing the user that the failures are almost
   990  		// definitely a provider bug.
   991  		_, failures, err := prov.Check(s.new.URN, s.old.Inputs, s.new.Inputs, preview, s.randomSeed)
   992  		if err != nil {
   993  			return rst, nil, err
   994  		}
   995  
   996  		// Print this warning before printing all the check failures to give better context.
   997  		if len(failures) != 0 {
   998  
   999  			// Based on if the user passed 'properties' or not we want to change the error message here.
  1000  			var errorMessage string
  1001  			if len(inputProperties) == 0 {
  1002  				ref, err := providers.ParseReference(s.Provider())
  1003  				contract.Assert(err == nil)
  1004  
  1005  				pkgName := ref.URN().Type().Name()
  1006  				errorMessage = fmt.Sprintf("This is almost certainly a bug in the `%s` provider.", pkgName)
  1007  			} else {
  1008  				errorMessage = "Try specifying a different set of properties to import with in the future."
  1009  			}
  1010  
  1011  			s.deployment.Diag().Warningf(diag.Message(s.new.URN,
  1012  				"One or more imported inputs failed to validate. %s "+
  1013  					"The import will still proceed, but you will need to edit the generated code after copying it into your program."),
  1014  				errorMessage)
  1015  		}
  1016  
  1017  		issueCheckFailures(s.deployment.Diag().Warningf, s.new, s.new.URN, failures)
  1018  
  1019  		s.diffs, s.detailedDiff = []resource.PropertyKey{}, map[string]plugin.PropertyDiff{}
  1020  		return rst, complete, err
  1021  	}
  1022  
  1023  	// Set inputs back to their old values (if any) for any "ignored" properties
  1024  	processedInputs, res := processIgnoreChanges(s.new.Inputs, s.old.Inputs, s.ignoreChanges)
  1025  	if res != nil {
  1026  		return resource.StatusOK, nil, res.Error()
  1027  	}
  1028  	s.new.Inputs = processedInputs
  1029  
  1030  	// Check the inputs using the provider inputs for defaults.
  1031  	inputs, failures, err := prov.Check(s.new.URN, s.old.Inputs, s.new.Inputs, preview, s.randomSeed)
  1032  	if err != nil {
  1033  		return rst, nil, err
  1034  	}
  1035  	if issueCheckErrors(s.deployment, s.new, s.new.URN, failures) {
  1036  		return rst, nil, errors.New("one or more inputs failed to validate")
  1037  	}
  1038  	s.new.Inputs = inputs
  1039  
  1040  	// Diff the user inputs against the provider inputs. If there are any differences, fail the import unless this step
  1041  	// is from an import deployment.
  1042  	diff, err := diffResource(s.new.URN, s.new.ID, s.old.Inputs, s.old.Outputs, s.new.Inputs, prov, preview,
  1043  		s.ignoreChanges)
  1044  	if err != nil {
  1045  		return rst, nil, err
  1046  	}
  1047  
  1048  	s.diffs, s.detailedDiff = diff.ChangedKeys, diff.DetailedDiff
  1049  
  1050  	if diff.Changes != plugin.DiffNone {
  1051  		const message = "inputs to import do not match the existing resource"
  1052  
  1053  		if preview {
  1054  			s.deployment.ctx.Diag.Warningf(diag.StreamMessage(s.new.URN,
  1055  				message+"; importing this resource will fail", 0))
  1056  		} else {
  1057  			err = errors.New(message)
  1058  		}
  1059  	}
  1060  
  1061  	// If we were asked to replace an existing, non-External resource, pend the deletion here.
  1062  	if err == nil && s.replacing {
  1063  		s.original.Delete = true
  1064  	}
  1065  
  1066  	return rst, complete, err
  1067  }
  1068  
  1069  const (
  1070  	OpSame                 display.StepOp = "same"                   // nothing to do.
  1071  	OpCreate               display.StepOp = "create"                 // creating a new resource.
  1072  	OpUpdate               display.StepOp = "update"                 // updating an existing resource.
  1073  	OpDelete               display.StepOp = "delete"                 // deleting an existing resource.
  1074  	OpReplace              display.StepOp = "replace"                // replacing a resource with a new one.
  1075  	OpCreateReplacement    display.StepOp = "create-replacement"     // creating a new resource for a replacement.
  1076  	OpDeleteReplaced       display.StepOp = "delete-replaced"        // deleting an existing resource after replacement.
  1077  	OpRead                 display.StepOp = "read"                   // reading an existing resource.
  1078  	OpReadReplacement      display.StepOp = "read-replacement"       // reading an existing resource for a replacement.
  1079  	OpRefresh              display.StepOp = "refresh"                // refreshing an existing resource.
  1080  	OpReadDiscard          display.StepOp = "discard"                // removing a resource that was read.
  1081  	OpDiscardReplaced      display.StepOp = "discard-replaced"       // discarding a read resource that was replaced.
  1082  	OpRemovePendingReplace display.StepOp = "remove-pending-replace" // removing a pending replace resource.
  1083  	OpImport               display.StepOp = "import"                 // import an existing resource.
  1084  	OpImportReplacement    display.StepOp = "import-replacement"     // replace an existing resource
  1085  	// with an imported resource.
  1086  )
  1087  
  1088  // StepOps contains the full set of step operation types.
  1089  var StepOps = []display.StepOp{
  1090  	OpSame,
  1091  	OpCreate,
  1092  	OpUpdate,
  1093  	OpDelete,
  1094  	OpReplace,
  1095  	OpCreateReplacement,
  1096  	OpDeleteReplaced,
  1097  	OpRead,
  1098  	OpReadReplacement,
  1099  	OpRefresh,
  1100  	OpReadDiscard,
  1101  	OpDiscardReplaced,
  1102  	OpRemovePendingReplace,
  1103  	OpImport,
  1104  	OpImportReplacement,
  1105  }
  1106  
  1107  // Color returns a suggested color for lines of this op type.
  1108  func Color(op display.StepOp) string {
  1109  	switch op {
  1110  	case OpSame:
  1111  		return colors.SpecUnimportant
  1112  	case OpCreate, OpImport:
  1113  		return colors.SpecCreate
  1114  	case OpDelete:
  1115  		return colors.SpecDelete
  1116  	case OpUpdate:
  1117  		return colors.SpecUpdate
  1118  	case OpReplace:
  1119  		return colors.SpecReplace
  1120  	case OpCreateReplacement:
  1121  		return colors.SpecCreateReplacement
  1122  	case OpDeleteReplaced:
  1123  		return colors.SpecDeleteReplaced
  1124  	case OpRead:
  1125  		return colors.SpecRead
  1126  	case OpReadReplacement, OpImportReplacement:
  1127  		return colors.SpecReplace
  1128  	case OpRefresh:
  1129  		return colors.SpecUpdate
  1130  	case OpReadDiscard, OpDiscardReplaced:
  1131  		return colors.SpecDelete
  1132  	default:
  1133  		contract.Failf("Unrecognized resource step op: '%v'", op)
  1134  		return ""
  1135  	}
  1136  }
  1137  
  1138  // ColorProgress returns a suggested coloring for lines of this of type which
  1139  // are progressing.
  1140  func ColorProgress(op display.StepOp) string {
  1141  	return colors.Bold + Color(op)
  1142  }
  1143  
  1144  // Prefix returns a suggested prefix for lines of this op type.
  1145  func Prefix(op display.StepOp, done bool) string {
  1146  	var color string
  1147  	if done {
  1148  		color = Color(op)
  1149  	} else {
  1150  		color = ColorProgress(op)
  1151  	}
  1152  	return color + RawPrefix(op)
  1153  }
  1154  
  1155  // RawPrefix returns the uncolorized prefix text.
  1156  func RawPrefix(op display.StepOp) string {
  1157  	switch op {
  1158  	case OpSame:
  1159  		return "  "
  1160  	case OpCreate:
  1161  		return "+ "
  1162  	case OpDelete:
  1163  		return "- "
  1164  	case OpUpdate:
  1165  		return "~ "
  1166  	case OpReplace:
  1167  		return "+-"
  1168  	case OpCreateReplacement:
  1169  		return "++"
  1170  	case OpDeleteReplaced:
  1171  		return "--"
  1172  	case OpRead:
  1173  		return "> "
  1174  	case OpReadReplacement:
  1175  		return ">>"
  1176  	case OpRefresh:
  1177  		return "~ "
  1178  	case OpReadDiscard:
  1179  		return "< "
  1180  	case OpDiscardReplaced:
  1181  		return "<<"
  1182  	case OpImport:
  1183  		return "= "
  1184  	case OpImportReplacement:
  1185  		return "=>"
  1186  	default:
  1187  		contract.Failf("Unrecognized resource step op: %v", op)
  1188  		return ""
  1189  	}
  1190  }
  1191  
  1192  func PastTense(op display.StepOp) string {
  1193  	switch op {
  1194  	case OpSame, OpCreate, OpReplace, OpCreateReplacement, OpUpdate, OpReadReplacement:
  1195  		return string(op) + "d"
  1196  	case OpRefresh:
  1197  		return "refreshed"
  1198  	case OpRead:
  1199  		return "read"
  1200  	case OpReadDiscard, OpDiscardReplaced:
  1201  		return "discarded"
  1202  	case OpDelete, OpDeleteReplaced:
  1203  		return "deleted"
  1204  	case OpImport, OpImportReplacement:
  1205  		return "imported"
  1206  	default:
  1207  		contract.Failf("Unexpected resource step op: %v", op)
  1208  		return ""
  1209  	}
  1210  }
  1211  
  1212  // Suffix returns a suggested suffix for lines of this op type.
  1213  func Suffix(op display.StepOp) string {
  1214  	switch op {
  1215  	case OpCreateReplacement, OpUpdate, OpReplace, OpReadReplacement, OpRefresh, OpImportReplacement:
  1216  		return colors.Reset // updates and replacements colorize individual lines; get has none
  1217  	}
  1218  	return ""
  1219  }
  1220  
  1221  // ConstrainedTo returns true if this operation is no more impactful than the constraint.
  1222  func ConstrainedTo(op display.StepOp, constraint display.StepOp) bool {
  1223  	var allowed []display.StepOp
  1224  	switch constraint {
  1225  	case OpSame, OpDelete, OpRead, OpReadReplacement, OpRefresh, OpReadDiscard, OpDiscardReplaced,
  1226  		OpRemovePendingReplace, OpImport, OpImportReplacement:
  1227  		allowed = []display.StepOp{constraint}
  1228  	case OpCreate:
  1229  		allowed = []display.StepOp{OpSame, OpCreate}
  1230  	case OpUpdate:
  1231  		allowed = []display.StepOp{OpSame, OpUpdate}
  1232  	case OpReplace, OpCreateReplacement, OpDeleteReplaced:
  1233  		allowed = []display.StepOp{OpSame, OpUpdate, constraint}
  1234  	}
  1235  	for _, candidate := range allowed {
  1236  		if candidate == op {
  1237  			return true
  1238  		}
  1239  	}
  1240  	return false
  1241  }
  1242  
  1243  // getProvider fetches the provider for the given step.
  1244  func getProvider(s Step) (plugin.Provider, error) {
  1245  	if providers.IsProviderType(s.Type()) {
  1246  		return s.Deployment().providers, nil
  1247  	}
  1248  	ref, err := providers.ParseReference(s.Provider())
  1249  	if err != nil {
  1250  		return nil, fmt.Errorf("bad provider reference '%v' for resource %v: %v", s.Provider(), s.URN(), err)
  1251  	}
  1252  	if providers.IsDenyDefaultsProvider(ref) {
  1253  		pkg := providers.GetDeniedDefaultProviderPkg(ref)
  1254  		msg := diag.GetDefaultProviderDenied(s.URN()).Message
  1255  		return nil, fmt.Errorf(msg, pkg, s.URN())
  1256  	}
  1257  	provider, ok := s.Deployment().GetProvider(ref)
  1258  	if !ok {
  1259  		return nil, fmt.Errorf("unknown provider '%v' for resource %v", s.Provider(), s.URN())
  1260  	}
  1261  	return provider, nil
  1262  }