github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/terraform/graph_walk_context.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/hashicorp/errwrap" 8 "github.com/hashicorp/terraform/dag" 9 ) 10 11 // ContextGraphWalker is the GraphWalker implementation used with the 12 // Context struct to walk and evaluate the graph. 13 type ContextGraphWalker struct { 14 NullGraphWalker 15 16 // Configurable values 17 Context *Context 18 Operation walkOperation 19 20 // Outputs, do not set these. Do not read these while the graph 21 // is being walked. 22 ValidationWarnings []string 23 ValidationErrors []error 24 25 errorLock sync.Mutex 26 once sync.Once 27 contexts map[string]*BuiltinEvalContext 28 contextLock sync.Mutex 29 providerCache map[string]ResourceProvider 30 providerConfigCache map[string]*ResourceConfig 31 providerLock sync.Mutex 32 provisionerCache map[string]ResourceProvisioner 33 provisionerLock sync.Mutex 34 } 35 36 func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext { 37 w.once.Do(w.init) 38 39 w.contextLock.Lock() 40 defer w.contextLock.Unlock() 41 42 // If we already have a context for this path cached, use that 43 key := PathCacheKey(g.Path) 44 if ctx, ok := w.contexts[key]; ok { 45 return ctx 46 } 47 48 // Variables should be our context variables, but these only apply 49 // to the root module. As we enter subgraphs, we don't want to set 50 // variables, which is set by the SetVariables EvalContext function. 51 variables := w.Context.variables 52 if len(g.Path) > 1 { 53 // We're in a submodule, the variables should be empty 54 variables = make(map[string]string) 55 } 56 57 ctx := &BuiltinEvalContext{ 58 PathValue: g.Path, 59 Hooks: w.Context.hooks, 60 InputValue: w.Context.uiInput, 61 Providers: w.Context.providers, 62 ProviderCache: w.providerCache, 63 ProviderConfigCache: w.providerConfigCache, 64 ProviderInputConfig: w.Context.providerInputConfig, 65 ProviderLock: &w.providerLock, 66 Provisioners: w.Context.provisioners, 67 ProvisionerCache: w.provisionerCache, 68 ProvisionerLock: &w.provisionerLock, 69 DiffValue: w.Context.diff, 70 DiffLock: &w.Context.diffLock, 71 StateValue: w.Context.state, 72 StateLock: &w.Context.stateLock, 73 Interpolater: &Interpolater{ 74 Operation: w.Operation, 75 Module: w.Context.module, 76 State: w.Context.state, 77 StateLock: &w.Context.stateLock, 78 Variables: variables, 79 }, 80 } 81 82 w.contexts[key] = ctx 83 return ctx 84 } 85 86 func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { 87 // Acquire a lock on the semaphore 88 w.Context.parallelSem.Acquire() 89 90 // We want to filter the evaluation tree to only include operations 91 // that belong in this operation. 92 return EvalFilter(n, EvalNodeFilterOp(w.Operation)) 93 } 94 95 func (w *ContextGraphWalker) ExitEvalTree( 96 v dag.Vertex, output interface{}, err error) error { 97 // Release the semaphore 98 w.Context.parallelSem.Release() 99 100 if err == nil { 101 return nil 102 } 103 104 // Acquire the lock because anything is going to require a lock. 105 w.errorLock.Lock() 106 defer w.errorLock.Unlock() 107 108 // Try to get a validation error out of it. If its not a validation 109 // error, then just record the normal error. 110 verr, ok := err.(*EvalValidateError) 111 if !ok { 112 return err 113 } 114 115 for _, msg := range verr.Warnings { 116 w.ValidationWarnings = append( 117 w.ValidationWarnings, 118 fmt.Sprintf("%s: %s", dag.VertexName(v), msg)) 119 } 120 for _, e := range verr.Errors { 121 w.ValidationErrors = append( 122 w.ValidationErrors, 123 errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e)) 124 } 125 126 return nil 127 } 128 129 func (w *ContextGraphWalker) init() { 130 w.contexts = make(map[string]*BuiltinEvalContext, 5) 131 w.providerCache = make(map[string]ResourceProvider, 5) 132 w.providerConfigCache = make(map[string]*ResourceConfig, 5) 133 w.provisionerCache = make(map[string]ResourceProvisioner, 5) 134 }