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

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/pulumi/terraform/pkg/checks"
     7  	"github.com/pulumi/terraform/pkg/configs"
     8  	"github.com/pulumi/terraform/pkg/instances"
     9  	"github.com/pulumi/terraform/pkg/plans"
    10  	"github.com/pulumi/terraform/pkg/refactoring"
    11  	"github.com/pulumi/terraform/pkg/states"
    12  	"github.com/pulumi/terraform/pkg/tfdiags"
    13  )
    14  
    15  // graphWalkOpts captures some transient values we use (and possibly mutate)
    16  // during a graph walk.
    17  //
    18  // The way these options get used unfortunately varies between the different
    19  // walkOperation types. This is a historical design wart that dates back to
    20  // us using the same graph structure for all operations; hopefully we'll
    21  // make the necessary differences between the walk types more explicit someday.
    22  type graphWalkOpts struct {
    23  	InputState *states.State
    24  	Changes    *plans.Changes
    25  	Config     *configs.Config
    26  
    27  	// PlanTimeCheckResults should be populated during the apply phase with
    28  	// the snapshot of check results that was generated during the plan step.
    29  	//
    30  	// This then propagates the decisions about which checkable objects exist
    31  	// from the plan phase into the apply phase without having to re-compute
    32  	// the module and resource expansion.
    33  	PlanTimeCheckResults *states.CheckResults
    34  
    35  	MoveResults refactoring.MoveResults
    36  }
    37  
    38  func (c *Context) walk(graph *Graph, operation walkOperation, opts *graphWalkOpts) (*ContextGraphWalker, tfdiags.Diagnostics) {
    39  	log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
    40  
    41  	walker := c.graphWalker(operation, opts)
    42  
    43  	// Watch for a stop so we can call the provider Stop() API.
    44  	watchStop, watchWait := c.watchStop(walker)
    45  
    46  	// Walk the real graph, this will block until it completes
    47  	diags := graph.Walk(walker)
    48  
    49  	// Close the channel so the watcher stops, and wait for it to return.
    50  	close(watchStop)
    51  	<-watchWait
    52  
    53  	return walker, diags
    54  }
    55  
    56  func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *ContextGraphWalker {
    57  	var state *states.SyncState
    58  	var refreshState *states.SyncState
    59  	var prevRunState *states.SyncState
    60  
    61  	// NOTE: None of the SyncState objects must directly wrap opts.InputState,
    62  	// because we use those to mutate the state object and opts.InputState
    63  	// belongs to our caller and thus we must treat it as immutable.
    64  	//
    65  	// To account for that, most of our SyncState values created below end up
    66  	// wrapping a _deep copy_ of opts.InputState instead.
    67  	inputState := opts.InputState
    68  	if inputState == nil {
    69  		// Lots of callers use nil to represent the "empty" case where we've
    70  		// not run Apply yet, so we tolerate that.
    71  		inputState = states.NewState()
    72  	}
    73  
    74  	switch operation {
    75  	case walkValidate:
    76  		// validate should not use any state
    77  		state = states.NewState().SyncWrapper()
    78  
    79  		// validate currently uses the plan graph, so we have to populate the
    80  		// refreshState and the prevRunState.
    81  		refreshState = states.NewState().SyncWrapper()
    82  		prevRunState = states.NewState().SyncWrapper()
    83  
    84  	case walkPlan, walkPlanDestroy, walkImport:
    85  		state = inputState.DeepCopy().SyncWrapper()
    86  		refreshState = inputState.DeepCopy().SyncWrapper()
    87  		prevRunState = inputState.DeepCopy().SyncWrapper()
    88  
    89  		// For both of our new states we'll discard the previous run's
    90  		// check results, since we can still refer to them from the
    91  		// prevRunState object if we need to.
    92  		state.DiscardCheckResults()
    93  		refreshState.DiscardCheckResults()
    94  
    95  	default:
    96  		state = inputState.DeepCopy().SyncWrapper()
    97  		// Only plan-like walks use refreshState and prevRunState
    98  
    99  		// Discard the input state's check results, because we should create
   100  		// a new set as a result of the graph walk.
   101  		state.DiscardCheckResults()
   102  	}
   103  
   104  	changes := opts.Changes
   105  	if changes == nil {
   106  		// Several of our non-plan walks end up sharing codepaths with the
   107  		// plan walk and thus expect to generate planned changes even though
   108  		// we don't care about them. To avoid those crashing, we'll just
   109  		// insert a placeholder changes object which'll get discarded
   110  		// afterwards.
   111  		changes = plans.NewChanges()
   112  	}
   113  
   114  	if opts.Config == nil {
   115  		panic("Context.graphWalker call without Config")
   116  	}
   117  
   118  	checkState := checks.NewState(opts.Config)
   119  	if opts.PlanTimeCheckResults != nil {
   120  		// We'll re-report all of the same objects we determined during the
   121  		// plan phase so that we can repeat the checks during the apply
   122  		// phase to finalize them.
   123  		for _, configElem := range opts.PlanTimeCheckResults.ConfigResults.Elems {
   124  			if configElem.Value.ObjectAddrsKnown() {
   125  				configAddr := configElem.Key
   126  				checkState.ReportCheckableObjects(configAddr, configElem.Value.ObjectResults.Keys())
   127  			}
   128  		}
   129  	}
   130  
   131  	return &ContextGraphWalker{
   132  		Context:          c,
   133  		State:            state,
   134  		Config:           opts.Config,
   135  		RefreshState:     refreshState,
   136  		PrevRunState:     prevRunState,
   137  		Changes:          changes.SyncWrapper(),
   138  		Checks:           checkState,
   139  		InstanceExpander: instances.NewExpander(),
   140  		MoveResults:      opts.MoveResults,
   141  		Operation:        operation,
   142  		StopContext:      c.runContext,
   143  	}
   144  }