]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "context" | |
bae9f6d2 JC |
5 | "log" |
6 | "sync" | |
7 | ||
107c1cdb ND |
8 | "github.com/zclconf/go-cty/cty" |
9 | ||
10 | "github.com/hashicorp/terraform/addrs" | |
11 | "github.com/hashicorp/terraform/configs/configschema" | |
bae9f6d2 | 12 | "github.com/hashicorp/terraform/dag" |
107c1cdb ND |
13 | "github.com/hashicorp/terraform/plans" |
14 | "github.com/hashicorp/terraform/providers" | |
15 | "github.com/hashicorp/terraform/provisioners" | |
16 | "github.com/hashicorp/terraform/states" | |
17 | "github.com/hashicorp/terraform/tfdiags" | |
bae9f6d2 JC |
18 | ) |
19 | ||
20 | // ContextGraphWalker is the GraphWalker implementation used with the | |
21 | // Context struct to walk and evaluate the graph. | |
22 | type ContextGraphWalker struct { | |
23 | NullGraphWalker | |
24 | ||
25 | // Configurable values | |
107c1cdb ND |
26 | Context *Context |
27 | State *states.SyncState // Used for safe concurrent access to state | |
28 | Changes *plans.ChangesSync // Used for safe concurrent writes to changes | |
29 | Operation walkOperation | |
30 | StopContext context.Context | |
31 | RootVariableValues InputValues | |
32 | ||
33 | // This is an output. Do not set this, nor read it while a graph walk | |
34 | // is in progress. | |
35 | NonFatalDiagnostics tfdiags.Diagnostics | |
36 | ||
37 | errorLock sync.Mutex | |
38 | once sync.Once | |
39 | contexts map[string]*BuiltinEvalContext | |
40 | contextLock sync.Mutex | |
41 | variableValues map[string]map[string]cty.Value | |
42 | variableValuesLock sync.Mutex | |
43 | providerCache map[string]providers.Interface | |
44 | providerSchemas map[string]*ProviderSchema | |
45 | providerLock sync.Mutex | |
46 | provisionerCache map[string]provisioners.Interface | |
47 | provisionerSchemas map[string]*configschema.Block | |
48 | provisionerLock sync.Mutex | |
bae9f6d2 JC |
49 | } |
50 | ||
107c1cdb | 51 | func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { |
bae9f6d2 JC |
52 | w.once.Do(w.init) |
53 | ||
54 | w.contextLock.Lock() | |
55 | defer w.contextLock.Unlock() | |
56 | ||
57 | // If we already have a context for this path cached, use that | |
107c1cdb | 58 | key := path.String() |
bae9f6d2 JC |
59 | if ctx, ok := w.contexts[key]; ok { |
60 | return ctx | |
61 | } | |
62 | ||
107c1cdb ND |
63 | // Our evaluator shares some locks with the main context and the walker |
64 | // so that we can safely run multiple evaluations at once across | |
65 | // different modules. | |
66 | evaluator := &Evaluator{ | |
67 | Meta: w.Context.meta, | |
68 | Config: w.Context.config, | |
69 | Operation: w.Operation, | |
70 | State: w.State, | |
71 | Changes: w.Changes, | |
72 | Schemas: w.Context.schemas, | |
73 | VariableValues: w.variableValues, | |
74 | VariableValuesLock: &w.variableValuesLock, | |
bae9f6d2 | 75 | } |
bae9f6d2 JC |
76 | |
77 | ctx := &BuiltinEvalContext{ | |
78 | StopContext: w.StopContext, | |
79 | PathValue: path, | |
80 | Hooks: w.Context.hooks, | |
81 | InputValue: w.Context.uiInput, | |
82 | Components: w.Context.components, | |
107c1cdb | 83 | Schemas: w.Context.schemas, |
bae9f6d2 | 84 | ProviderCache: w.providerCache, |
bae9f6d2 JC |
85 | ProviderInputConfig: w.Context.providerInputConfig, |
86 | ProviderLock: &w.providerLock, | |
87 | ProvisionerCache: w.provisionerCache, | |
88 | ProvisionerLock: &w.provisionerLock, | |
107c1cdb ND |
89 | ChangesValue: w.Changes, |
90 | StateValue: w.State, | |
91 | Evaluator: evaluator, | |
92 | VariableValues: w.variableValues, | |
93 | VariableValuesLock: &w.variableValuesLock, | |
bae9f6d2 JC |
94 | } |
95 | ||
96 | w.contexts[key] = ctx | |
97 | return ctx | |
98 | } | |
99 | ||
100 | func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { | |
107c1cdb | 101 | log.Printf("[TRACE] [%s] Entering eval tree: %s", w.Operation, dag.VertexName(v)) |
bae9f6d2 JC |
102 | |
103 | // Acquire a lock on the semaphore | |
104 | w.Context.parallelSem.Acquire() | |
105 | ||
106 | // We want to filter the evaluation tree to only include operations | |
107 | // that belong in this operation. | |
108 | return EvalFilter(n, EvalNodeFilterOp(w.Operation)) | |
109 | } | |
110 | ||
107c1cdb ND |
111 | func (w *ContextGraphWalker) ExitEvalTree(v dag.Vertex, output interface{}, err error) tfdiags.Diagnostics { |
112 | log.Printf("[TRACE] [%s] Exiting eval tree: %s", w.Operation, dag.VertexName(v)) | |
bae9f6d2 JC |
113 | |
114 | // Release the semaphore | |
115 | w.Context.parallelSem.Release() | |
116 | ||
117 | if err == nil { | |
118 | return nil | |
119 | } | |
120 | ||
121 | // Acquire the lock because anything is going to require a lock. | |
122 | w.errorLock.Lock() | |
123 | defer w.errorLock.Unlock() | |
124 | ||
107c1cdb ND |
125 | // If the error is non-fatal then we'll accumulate its diagnostics in our |
126 | // non-fatal list, rather than returning it directly, so that the graph | |
127 | // walk can continue. | |
128 | if nferr, ok := err.(tfdiags.NonFatalError); ok { | |
129 | log.Printf("[WARN] %s: %s", dag.VertexName(v), nferr) | |
130 | w.NonFatalDiagnostics = w.NonFatalDiagnostics.Append(nferr.Diagnostics) | |
131 | return nil | |
bae9f6d2 JC |
132 | } |
133 | ||
107c1cdb ND |
134 | // Otherwise, we'll let our usual diagnostics machinery figure out how to |
135 | // unpack this as one or more diagnostic messages and return that. If we | |
136 | // get down here then the returned diagnostics will contain at least one | |
137 | // error, causing the graph walk to halt. | |
138 | var diags tfdiags.Diagnostics | |
139 | diags = diags.Append(err) | |
140 | return diags | |
bae9f6d2 JC |
141 | } |
142 | ||
143 | func (w *ContextGraphWalker) init() { | |
107c1cdb ND |
144 | w.contexts = make(map[string]*BuiltinEvalContext) |
145 | w.providerCache = make(map[string]providers.Interface) | |
146 | w.providerSchemas = make(map[string]*ProviderSchema) | |
147 | w.provisionerCache = make(map[string]provisioners.Interface) | |
148 | w.provisionerSchemas = make(map[string]*configschema.Block) | |
149 | w.variableValues = make(map[string]map[string]cty.Value) | |
150 | ||
151 | // Populate root module variable values. Other modules will be populated | |
152 | // during the graph walk. | |
153 | w.variableValues[""] = make(map[string]cty.Value) | |
154 | for k, iv := range w.RootVariableValues { | |
155 | w.variableValues[""][k] = iv.Value | |
156 | } | |
bae9f6d2 | 157 | } |