github.com/leighwaller/terraform@v0.11.12-beta1/terraform/graph_walk_context.go (about) 1 package terraform 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "sync" 8 9 "github.com/hashicorp/errwrap" 10 "github.com/hashicorp/terraform/dag" 11 ) 12 13 // ContextGraphWalker is the GraphWalker implementation used with the 14 // Context struct to walk and evaluate the graph. 15 type ContextGraphWalker struct { 16 NullGraphWalker 17 18 // Configurable values 19 Context *Context 20 Operation walkOperation 21 StopContext context.Context 22 23 // Outputs, do not set these. Do not read these while the graph 24 // is being walked. 25 ValidationWarnings []string 26 ValidationErrors []error 27 28 errorLock sync.Mutex 29 once sync.Once 30 contexts map[string]*BuiltinEvalContext 31 contextLock sync.Mutex 32 interpolaterVars map[string]map[string]interface{} 33 interpolaterVarLock sync.Mutex 34 providerCache map[string]ResourceProvider 35 providerLock sync.Mutex 36 provisionerCache map[string]ResourceProvisioner 37 provisionerLock sync.Mutex 38 } 39 40 func (w *ContextGraphWalker) EnterPath(path []string) EvalContext { 41 w.once.Do(w.init) 42 43 w.contextLock.Lock() 44 defer w.contextLock.Unlock() 45 46 // If we already have a context for this path cached, use that 47 key := PathCacheKey(path) 48 if ctx, ok := w.contexts[key]; ok { 49 return ctx 50 } 51 52 // Setup the variables for this interpolater 53 variables := make(map[string]interface{}) 54 if len(path) <= 1 { 55 for k, v := range w.Context.variables { 56 variables[k] = v 57 } 58 } 59 w.interpolaterVarLock.Lock() 60 if m, ok := w.interpolaterVars[key]; ok { 61 for k, v := range m { 62 variables[k] = v 63 } 64 } 65 w.interpolaterVars[key] = variables 66 w.interpolaterVarLock.Unlock() 67 68 ctx := &BuiltinEvalContext{ 69 StopContext: w.StopContext, 70 PathValue: path, 71 Hooks: w.Context.hooks, 72 InputValue: w.Context.uiInput, 73 Components: w.Context.components, 74 ProviderCache: w.providerCache, 75 ProviderInputConfig: w.Context.providerInputConfig, 76 ProviderLock: &w.providerLock, 77 ProvisionerCache: w.provisionerCache, 78 ProvisionerLock: &w.provisionerLock, 79 DiffValue: w.Context.diff, 80 DiffLock: &w.Context.diffLock, 81 StateValue: w.Context.state, 82 StateLock: &w.Context.stateLock, 83 Interpolater: &Interpolater{ 84 Operation: w.Operation, 85 Meta: w.Context.meta, 86 Module: w.Context.module, 87 State: w.Context.state, 88 StateLock: &w.Context.stateLock, 89 VariableValues: variables, 90 VariableValuesLock: &w.interpolaterVarLock, 91 }, 92 InterpolaterVars: w.interpolaterVars, 93 InterpolaterVarLock: &w.interpolaterVarLock, 94 } 95 96 w.contexts[key] = ctx 97 return ctx 98 } 99 100 func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { 101 log.Printf("[TRACE] [%s] Entering eval tree: %s", 102 w.Operation, dag.VertexName(v)) 103 104 // Acquire a lock on the semaphore 105 w.Context.parallelSem.Acquire() 106 107 // We want to filter the evaluation tree to only include operations 108 // that belong in this operation. 109 return EvalFilter(n, EvalNodeFilterOp(w.Operation)) 110 } 111 112 func (w *ContextGraphWalker) ExitEvalTree( 113 v dag.Vertex, output interface{}, err error) error { 114 log.Printf("[TRACE] [%s] Exiting eval tree: %s", 115 w.Operation, dag.VertexName(v)) 116 117 // Release the semaphore 118 w.Context.parallelSem.Release() 119 120 if err == nil { 121 return nil 122 } 123 124 // Acquire the lock because anything is going to require a lock. 125 w.errorLock.Lock() 126 defer w.errorLock.Unlock() 127 128 // Try to get a validation error out of it. If its not a validation 129 // error, then just record the normal error. 130 verr, ok := err.(*EvalValidateError) 131 if !ok { 132 return err 133 } 134 135 for _, msg := range verr.Warnings { 136 w.ValidationWarnings = append( 137 w.ValidationWarnings, 138 fmt.Sprintf("%s: %s", dag.VertexName(v), msg)) 139 } 140 for _, e := range verr.Errors { 141 w.ValidationErrors = append( 142 w.ValidationErrors, 143 errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e)) 144 } 145 146 return nil 147 } 148 149 func (w *ContextGraphWalker) init() { 150 w.contexts = make(map[string]*BuiltinEvalContext, 5) 151 w.providerCache = make(map[string]ResourceProvider, 5) 152 w.provisionerCache = make(map[string]ResourceProvisioner, 5) 153 w.interpolaterVars = make(map[string]map[string]interface{}, 5) 154 }