github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/terraform/context_walk.go (about)

     1  package terraform
     2  
     3  import (
     4  	"log"
     5  
     6  	"github.com/hashicorp/terraform/internal/addrs"
     7  	"github.com/hashicorp/terraform/internal/configs"
     8  	"github.com/hashicorp/terraform/internal/instances"
     9  	"github.com/hashicorp/terraform/internal/plans"
    10  	"github.com/hashicorp/terraform/internal/refactoring"
    11  	"github.com/hashicorp/terraform/internal/states"
    12  	"github.com/hashicorp/terraform/internal/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  	RootVariableValues InputValues
    28  	MoveResults        map[addrs.UniqueKey]refactoring.MoveResult
    29  }
    30  
    31  func (c *Context) walk(graph *Graph, operation walkOperation, opts *graphWalkOpts) (*ContextGraphWalker, tfdiags.Diagnostics) {
    32  	log.Printf("[DEBUG] Starting graph walk: %s", operation.String())
    33  
    34  	walker := c.graphWalker(operation, opts)
    35  
    36  	// Watch for a stop so we can call the provider Stop() API.
    37  	watchStop, watchWait := c.watchStop(walker)
    38  
    39  	// Walk the real graph, this will block until it completes
    40  	diags := graph.Walk(walker)
    41  
    42  	// Close the channel so the watcher stops, and wait for it to return.
    43  	close(watchStop)
    44  	<-watchWait
    45  
    46  	return walker, diags
    47  }
    48  
    49  func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *ContextGraphWalker {
    50  	var state *states.SyncState
    51  	var refreshState *states.SyncState
    52  	var prevRunState *states.SyncState
    53  
    54  	// NOTE: None of the SyncState objects must directly wrap opts.InputState,
    55  	// because we use those to mutate the state object and opts.InputState
    56  	// belongs to our caller and thus we must treat it as immutable.
    57  	//
    58  	// To account for that, most of our SyncState values created below end up
    59  	// wrapping a _deep copy_ of opts.InputState instead.
    60  	inputState := opts.InputState
    61  	if inputState == nil {
    62  		// Lots of callers use nil to represent the "empty" case where we've
    63  		// not run Apply yet, so we tolerate that.
    64  		inputState = states.NewState()
    65  	}
    66  
    67  	switch operation {
    68  	case walkValidate:
    69  		// validate should not use any state
    70  		state = states.NewState().SyncWrapper()
    71  
    72  		// validate currently uses the plan graph, so we have to populate the
    73  		// refreshState and the prevRunState.
    74  		refreshState = states.NewState().SyncWrapper()
    75  		prevRunState = states.NewState().SyncWrapper()
    76  
    77  	case walkPlan, walkPlanDestroy:
    78  		state = inputState.DeepCopy().SyncWrapper()
    79  		refreshState = inputState.DeepCopy().SyncWrapper()
    80  		prevRunState = inputState.DeepCopy().SyncWrapper()
    81  
    82  	default:
    83  		state = inputState.DeepCopy().SyncWrapper()
    84  		// Only plan-like walks use refreshState and prevRunState
    85  	}
    86  
    87  	changes := opts.Changes
    88  	if changes == nil {
    89  		// Several of our non-plan walks end up sharing codepaths with the
    90  		// plan walk and thus expect to generate planned changes even though
    91  		// we don't care about them. To avoid those crashing, we'll just
    92  		// insert a placeholder changes object which'll get discarded
    93  		// afterwards.
    94  		changes = plans.NewChanges()
    95  	}
    96  
    97  	if opts.Config == nil {
    98  		panic("Context.graphWalker call without Config")
    99  	}
   100  
   101  	return &ContextGraphWalker{
   102  		Context:            c,
   103  		State:              state,
   104  		Config:             opts.Config,
   105  		RefreshState:       refreshState,
   106  		PrevRunState:       prevRunState,
   107  		Changes:            changes.SyncWrapper(),
   108  		InstanceExpander:   instances.NewExpander(),
   109  		MoveResults:        opts.MoveResults,
   110  		Operation:          operation,
   111  		StopContext:        c.runContext,
   112  		RootVariableValues: opts.RootVariableValues,
   113  	}
   114  }