github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/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 providerConfigCache map[string]*ResourceConfig 36 providerLock sync.Mutex 37 provisionerCache map[string]ResourceProvisioner 38 provisionerLock sync.Mutex 39 } 40 41 func (w *ContextGraphWalker) EnterPath(path []string) EvalContext { 42 w.once.Do(w.init) 43 44 w.contextLock.Lock() 45 defer w.contextLock.Unlock() 46 47 // If we already have a context for this path cached, use that 48 key := PathCacheKey(path) 49 if ctx, ok := w.contexts[key]; ok { 50 return ctx 51 } 52 53 // Setup the variables for this interpolater 54 variables := make(map[string]interface{}) 55 if len(path) <= 1 { 56 for k, v := range w.Context.variables { 57 variables[k] = v 58 } 59 } 60 w.interpolaterVarLock.Lock() 61 if m, ok := w.interpolaterVars[key]; ok { 62 for k, v := range m { 63 variables[k] = v 64 } 65 } 66 w.interpolaterVars[key] = variables 67 w.interpolaterVarLock.Unlock() 68 69 ctx := &BuiltinEvalContext{ 70 StopContext: w.StopContext, 71 PathValue: path, 72 Hooks: w.Context.hooks, 73 InputValue: w.Context.uiInput, 74 Components: w.Context.components, 75 ProviderCache: w.providerCache, 76 ProviderConfigCache: w.providerConfigCache, 77 ProviderInputConfig: w.Context.providerInputConfig, 78 ProviderLock: &w.providerLock, 79 ProvisionerCache: w.provisionerCache, 80 ProvisionerLock: &w.provisionerLock, 81 DiffValue: w.Context.diff, 82 DiffLock: &w.Context.diffLock, 83 StateValue: w.Context.state, 84 StateLock: &w.Context.stateLock, 85 Interpolater: &Interpolater{ 86 Operation: w.Operation, 87 Meta: w.Context.meta, 88 Module: w.Context.module, 89 State: w.Context.state, 90 StateLock: &w.Context.stateLock, 91 VariableValues: variables, 92 VariableValuesLock: &w.interpolaterVarLock, 93 }, 94 InterpolaterVars: w.interpolaterVars, 95 InterpolaterVarLock: &w.interpolaterVarLock, 96 } 97 98 w.contexts[key] = ctx 99 return ctx 100 } 101 102 func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { 103 log.Printf("[TRACE] [%s] Entering eval tree: %s", 104 w.Operation, dag.VertexName(v)) 105 106 // Acquire a lock on the semaphore 107 w.Context.parallelSem.Acquire() 108 109 // We want to filter the evaluation tree to only include operations 110 // that belong in this operation. 111 return EvalFilter(n, EvalNodeFilterOp(w.Operation)) 112 } 113 114 func (w *ContextGraphWalker) ExitEvalTree( 115 v dag.Vertex, output interface{}, err error) error { 116 log.Printf("[TRACE] [%s] Exiting eval tree: %s", 117 w.Operation, dag.VertexName(v)) 118 119 // Release the semaphore 120 w.Context.parallelSem.Release() 121 122 if err == nil { 123 return nil 124 } 125 126 // Acquire the lock because anything is going to require a lock. 127 w.errorLock.Lock() 128 defer w.errorLock.Unlock() 129 130 // Try to get a validation error out of it. If its not a validation 131 // error, then just record the normal error. 132 verr, ok := err.(*EvalValidateError) 133 if !ok { 134 return err 135 } 136 137 for _, msg := range verr.Warnings { 138 w.ValidationWarnings = append( 139 w.ValidationWarnings, 140 fmt.Sprintf("%s: %s", dag.VertexName(v), msg)) 141 } 142 for _, e := range verr.Errors { 143 w.ValidationErrors = append( 144 w.ValidationErrors, 145 errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e)) 146 } 147 148 return nil 149 } 150 151 func (w *ContextGraphWalker) init() { 152 w.contexts = make(map[string]*BuiltinEvalContext, 5) 153 w.providerCache = make(map[string]ResourceProvider, 5) 154 w.providerConfigCache = make(map[string]*ResourceConfig, 5) 155 w.provisionerCache = make(map[string]ResourceProvisioner, 5) 156 w.interpolaterVars = make(map[string]map[string]interface{}, 5) 157 }