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