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 }