]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "context" | |
5 | "fmt" | |
6 | "log" | |
7 | "sort" | |
8 | "strings" | |
9 | "sync" | |
10 | ||
11 | "github.com/hashicorp/go-multierror" | |
12 | "github.com/hashicorp/hcl" | |
13 | "github.com/hashicorp/terraform/config" | |
14 | "github.com/hashicorp/terraform/config/module" | |
15 | "github.com/hashicorp/terraform/helper/experiment" | |
16 | ) | |
17 | ||
18 | // InputMode defines what sort of input will be asked for when Input | |
19 | // is called on Context. | |
20 | type InputMode byte | |
21 | ||
22 | const ( | |
23 | // InputModeVar asks for all variables | |
24 | InputModeVar InputMode = 1 << iota | |
25 | ||
26 | // InputModeVarUnset asks for variables which are not set yet. | |
27 | // InputModeVar must be set for this to have an effect. | |
28 | InputModeVarUnset | |
29 | ||
30 | // InputModeProvider asks for provider variables | |
31 | InputModeProvider | |
32 | ||
33 | // InputModeStd is the standard operating mode and asks for both variables | |
34 | // and providers. | |
35 | InputModeStd = InputModeVar | InputModeProvider | |
36 | ) | |
37 | ||
38 | var ( | |
39 | // contextFailOnShadowError will cause Context operations to return | |
40 | // errors when shadow operations fail. This is only used for testing. | |
41 | contextFailOnShadowError = false | |
42 | ||
43 | // contextTestDeepCopyOnPlan will perform a Diff DeepCopy on every | |
44 | // Plan operation, effectively testing the Diff DeepCopy whenever | |
45 | // a Plan occurs. This is enabled for tests. | |
46 | contextTestDeepCopyOnPlan = false | |
47 | ) | |
48 | ||
49 | // ContextOpts are the user-configurable options to create a context with | |
50 | // NewContext. | |
51 | type ContextOpts struct { | |
52 | Meta *ContextMeta | |
53 | Destroy bool | |
54 | Diff *Diff | |
55 | Hooks []Hook | |
56 | Module *module.Tree | |
57 | Parallelism int | |
58 | State *State | |
59 | StateFutureAllowed bool | |
60 | Providers map[string]ResourceProviderFactory | |
61 | Provisioners map[string]ResourceProvisionerFactory | |
62 | Shadow bool | |
63 | Targets []string | |
64 | Variables map[string]interface{} | |
65 | ||
66 | UIInput UIInput | |
67 | } | |
68 | ||
69 | // ContextMeta is metadata about the running context. This is information | |
70 | // that this package or structure cannot determine on its own but exposes | |
71 | // into Terraform in various ways. This must be provided by the Context | |
72 | // initializer. | |
73 | type ContextMeta struct { | |
74 | Env string // Env is the state environment | |
75 | } | |
76 | ||
77 | // Context represents all the context that Terraform needs in order to | |
78 | // perform operations on infrastructure. This structure is built using | |
79 | // NewContext. See the documentation for that. | |
80 | // | |
81 | // Extra functions on Context can be found in context_*.go files. | |
82 | type Context struct { | |
83 | // Maintainer note: Anytime this struct is changed, please verify | |
84 | // that newShadowContext still does the right thing. Tests should | |
85 | // fail regardless but putting this note here as well. | |
86 | ||
87 | components contextComponentFactory | |
88 | destroy bool | |
89 | diff *Diff | |
90 | diffLock sync.RWMutex | |
91 | hooks []Hook | |
92 | meta *ContextMeta | |
93 | module *module.Tree | |
94 | sh *stopHook | |
95 | shadow bool | |
96 | state *State | |
97 | stateLock sync.RWMutex | |
98 | targets []string | |
99 | uiInput UIInput | |
100 | variables map[string]interface{} | |
101 | ||
102 | l sync.Mutex // Lock acquired during any task | |
103 | parallelSem Semaphore | |
104 | providerInputConfig map[string]map[string]interface{} | |
105 | runLock sync.Mutex | |
106 | runCond *sync.Cond | |
107 | runContext context.Context | |
108 | runContextCancel context.CancelFunc | |
109 | shadowErr error | |
110 | } | |
111 | ||
112 | // NewContext creates a new Context structure. | |
113 | // | |
114 | // Once a Context is creator, the pointer values within ContextOpts | |
115 | // should not be mutated in any way, since the pointers are copied, not | |
116 | // the values themselves. | |
117 | func NewContext(opts *ContextOpts) (*Context, error) { | |
118 | // Validate the version requirement if it is given | |
119 | if opts.Module != nil { | |
120 | if err := checkRequiredVersion(opts.Module); err != nil { | |
121 | return nil, err | |
122 | } | |
123 | } | |
124 | ||
125 | // Copy all the hooks and add our stop hook. We don't append directly | |
126 | // to the Config so that we're not modifying that in-place. | |
127 | sh := new(stopHook) | |
128 | hooks := make([]Hook, len(opts.Hooks)+1) | |
129 | copy(hooks, opts.Hooks) | |
130 | hooks[len(opts.Hooks)] = sh | |
131 | ||
132 | state := opts.State | |
133 | if state == nil { | |
134 | state = new(State) | |
135 | state.init() | |
136 | } | |
137 | ||
138 | // If our state is from the future, then error. Callers can avoid | |
139 | // this error by explicitly setting `StateFutureAllowed`. | |
140 | if !opts.StateFutureAllowed && state.FromFutureTerraform() { | |
141 | return nil, fmt.Errorf( | |
142 | "Terraform doesn't allow running any operations against a state\n"+ | |
143 | "that was written by a future Terraform version. The state is\n"+ | |
144 | "reporting it is written by Terraform '%s'.\n\n"+ | |
145 | "Please run at least that version of Terraform to continue.", | |
146 | state.TFVersion) | |
147 | } | |
148 | ||
149 | // Explicitly reset our state version to our current version so that | |
150 | // any operations we do will write out that our latest version | |
151 | // has run. | |
152 | state.TFVersion = Version | |
153 | ||
154 | // Determine parallelism, default to 10. We do this both to limit | |
155 | // CPU pressure but also to have an extra guard against rate throttling | |
156 | // from providers. | |
157 | par := opts.Parallelism | |
158 | if par == 0 { | |
159 | par = 10 | |
160 | } | |
161 | ||
162 | // Set up the variables in the following sequence: | |
163 | // 0 - Take default values from the configuration | |
164 | // 1 - Take values from TF_VAR_x environment variables | |
165 | // 2 - Take values specified in -var flags, overriding values | |
166 | // set by environment variables if necessary. This includes | |
167 | // values taken from -var-file in addition. | |
168 | variables := make(map[string]interface{}) | |
169 | ||
170 | if opts.Module != nil { | |
171 | var err error | |
172 | variables, err = Variables(opts.Module, opts.Variables) | |
173 | if err != nil { | |
174 | return nil, err | |
175 | } | |
176 | } | |
177 | ||
178 | diff := opts.Diff | |
179 | if diff == nil { | |
180 | diff = &Diff{} | |
181 | } | |
182 | ||
183 | return &Context{ | |
184 | components: &basicComponentFactory{ | |
185 | providers: opts.Providers, | |
186 | provisioners: opts.Provisioners, | |
187 | }, | |
188 | destroy: opts.Destroy, | |
189 | diff: diff, | |
190 | hooks: hooks, | |
191 | meta: opts.Meta, | |
192 | module: opts.Module, | |
193 | shadow: opts.Shadow, | |
194 | state: state, | |
195 | targets: opts.Targets, | |
196 | uiInput: opts.UIInput, | |
197 | variables: variables, | |
198 | ||
199 | parallelSem: NewSemaphore(par), | |
200 | providerInputConfig: make(map[string]map[string]interface{}), | |
201 | sh: sh, | |
202 | }, nil | |
203 | } | |
204 | ||
205 | type ContextGraphOpts struct { | |
206 | // If true, validates the graph structure (checks for cycles). | |
207 | Validate bool | |
208 | ||
209 | // Legacy graphs only: won't prune the graph | |
210 | Verbose bool | |
211 | } | |
212 | ||
213 | // Graph returns the graph used for the given operation type. | |
214 | // | |
215 | // The most extensive or complex graph type is GraphTypePlan. | |
216 | func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) { | |
217 | if opts == nil { | |
218 | opts = &ContextGraphOpts{Validate: true} | |
219 | } | |
220 | ||
221 | log.Printf("[INFO] terraform: building graph: %s", typ) | |
222 | switch typ { | |
223 | case GraphTypeApply: | |
224 | return (&ApplyGraphBuilder{ | |
225 | Module: c.module, | |
226 | Diff: c.diff, | |
227 | State: c.state, | |
228 | Providers: c.components.ResourceProviders(), | |
229 | Provisioners: c.components.ResourceProvisioners(), | |
230 | Targets: c.targets, | |
231 | Destroy: c.destroy, | |
232 | Validate: opts.Validate, | |
233 | }).Build(RootModulePath) | |
234 | ||
235 | case GraphTypeInput: | |
236 | // The input graph is just a slightly modified plan graph | |
237 | fallthrough | |
238 | case GraphTypeValidate: | |
239 | // The validate graph is just a slightly modified plan graph | |
240 | fallthrough | |
241 | case GraphTypePlan: | |
242 | // Create the plan graph builder | |
243 | p := &PlanGraphBuilder{ | |
244 | Module: c.module, | |
245 | State: c.state, | |
246 | Providers: c.components.ResourceProviders(), | |
247 | Targets: c.targets, | |
248 | Validate: opts.Validate, | |
249 | } | |
250 | ||
251 | // Some special cases for other graph types shared with plan currently | |
252 | var b GraphBuilder = p | |
253 | switch typ { | |
254 | case GraphTypeInput: | |
255 | b = InputGraphBuilder(p) | |
256 | case GraphTypeValidate: | |
257 | // We need to set the provisioners so those can be validated | |
258 | p.Provisioners = c.components.ResourceProvisioners() | |
259 | ||
260 | b = ValidateGraphBuilder(p) | |
261 | } | |
262 | ||
263 | return b.Build(RootModulePath) | |
264 | ||
265 | case GraphTypePlanDestroy: | |
266 | return (&DestroyPlanGraphBuilder{ | |
267 | Module: c.module, | |
268 | State: c.state, | |
269 | Targets: c.targets, | |
270 | Validate: opts.Validate, | |
271 | }).Build(RootModulePath) | |
272 | ||
273 | case GraphTypeRefresh: | |
274 | return (&RefreshGraphBuilder{ | |
275 | Module: c.module, | |
276 | State: c.state, | |
277 | Providers: c.components.ResourceProviders(), | |
278 | Targets: c.targets, | |
279 | Validate: opts.Validate, | |
280 | }).Build(RootModulePath) | |
281 | } | |
282 | ||
283 | return nil, fmt.Errorf("unknown graph type: %s", typ) | |
284 | } | |
285 | ||
286 | // ShadowError returns any errors caught during a shadow operation. | |
287 | // | |
288 | // A shadow operation is an operation run in parallel to a real operation | |
289 | // that performs the same tasks using new logic on copied state. The results | |
290 | // are compared to ensure that the new logic works the same as the old logic. | |
291 | // The shadow never affects the real operation or return values. | |
292 | // | |
293 | // The result of the shadow operation are only available through this function | |
294 | // call after a real operation is complete. | |
295 | // | |
296 | // For API consumers of Context, you can safely ignore this function | |
297 | // completely if you have no interest in helping report experimental feature | |
298 | // errors to Terraform maintainers. Otherwise, please call this function | |
299 | // after every operation and report this to the user. | |
300 | // | |
301 | // IMPORTANT: Shadow errors are _never_ critical: they _never_ affect | |
302 | // the real state or result of a real operation. They are purely informational | |
303 | // to assist in future Terraform versions being more stable. Please message | |
304 | // this effectively to the end user. | |
305 | // | |
306 | // This must be called only when no other operation is running (refresh, | |
307 | // plan, etc.). The result can be used in parallel to any other operation | |
308 | // running. | |
309 | func (c *Context) ShadowError() error { | |
310 | return c.shadowErr | |
311 | } | |
312 | ||
313 | // State returns a copy of the current state associated with this context. | |
314 | // | |
315 | // This cannot safely be called in parallel with any other Context function. | |
316 | func (c *Context) State() *State { | |
317 | return c.state.DeepCopy() | |
318 | } | |
319 | ||
320 | // Interpolater returns an Interpolater built on a copy of the state | |
321 | // that can be used to test interpolation values. | |
322 | func (c *Context) Interpolater() *Interpolater { | |
323 | var varLock sync.Mutex | |
324 | var stateLock sync.RWMutex | |
325 | return &Interpolater{ | |
326 | Operation: walkApply, | |
327 | Meta: c.meta, | |
328 | Module: c.module, | |
329 | State: c.state.DeepCopy(), | |
330 | StateLock: &stateLock, | |
331 | VariableValues: c.variables, | |
332 | VariableValuesLock: &varLock, | |
333 | } | |
334 | } | |
335 | ||
336 | // Input asks for input to fill variables and provider configurations. | |
337 | // This modifies the configuration in-place, so asking for Input twice | |
338 | // may result in different UI output showing different current values. | |
339 | func (c *Context) Input(mode InputMode) error { | |
340 | defer c.acquireRun("input")() | |
341 | ||
342 | if mode&InputModeVar != 0 { | |
343 | // Walk the variables first for the root module. We walk them in | |
344 | // alphabetical order for UX reasons. | |
345 | rootConf := c.module.Config() | |
346 | names := make([]string, len(rootConf.Variables)) | |
347 | m := make(map[string]*config.Variable) | |
348 | for i, v := range rootConf.Variables { | |
349 | names[i] = v.Name | |
350 | m[v.Name] = v | |
351 | } | |
352 | sort.Strings(names) | |
353 | for _, n := range names { | |
354 | // If we only care about unset variables, then if the variable | |
355 | // is set, continue on. | |
356 | if mode&InputModeVarUnset != 0 { | |
357 | if _, ok := c.variables[n]; ok { | |
358 | continue | |
359 | } | |
360 | } | |
361 | ||
362 | var valueType config.VariableType | |
363 | ||
364 | v := m[n] | |
365 | switch valueType = v.Type(); valueType { | |
366 | case config.VariableTypeUnknown: | |
367 | continue | |
368 | case config.VariableTypeMap: | |
369 | // OK | |
370 | case config.VariableTypeList: | |
371 | // OK | |
372 | case config.VariableTypeString: | |
373 | // OK | |
374 | default: | |
375 | panic(fmt.Sprintf("Unknown variable type: %#v", v.Type())) | |
376 | } | |
377 | ||
378 | // If the variable is not already set, and the variable defines a | |
379 | // default, use that for the value. | |
380 | if _, ok := c.variables[n]; !ok { | |
381 | if v.Default != nil { | |
382 | c.variables[n] = v.Default.(string) | |
383 | continue | |
384 | } | |
385 | } | |
386 | ||
387 | // this should only happen during tests | |
388 | if c.uiInput == nil { | |
389 | log.Println("[WARN] Content.uiInput is nil") | |
390 | continue | |
391 | } | |
392 | ||
393 | // Ask the user for a value for this variable | |
394 | var value string | |
395 | retry := 0 | |
396 | for { | |
397 | var err error | |
398 | value, err = c.uiInput.Input(&InputOpts{ | |
399 | Id: fmt.Sprintf("var.%s", n), | |
400 | Query: fmt.Sprintf("var.%s", n), | |
401 | Description: v.Description, | |
402 | }) | |
403 | if err != nil { | |
404 | return fmt.Errorf( | |
405 | "Error asking for %s: %s", n, err) | |
406 | } | |
407 | ||
408 | if value == "" && v.Required() { | |
409 | // Redo if it is required, but abort if we keep getting | |
410 | // blank entries | |
411 | if retry > 2 { | |
412 | return fmt.Errorf("missing required value for %q", n) | |
413 | } | |
414 | retry++ | |
415 | continue | |
416 | } | |
417 | ||
418 | break | |
419 | } | |
420 | ||
421 | // no value provided, so don't set the variable at all | |
422 | if value == "" { | |
423 | continue | |
424 | } | |
425 | ||
426 | decoded, err := parseVariableAsHCL(n, value, valueType) | |
427 | if err != nil { | |
428 | return err | |
429 | } | |
430 | ||
431 | if decoded != nil { | |
432 | c.variables[n] = decoded | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
437 | if mode&InputModeProvider != 0 { | |
438 | // Build the graph | |
439 | graph, err := c.Graph(GraphTypeInput, nil) | |
440 | if err != nil { | |
441 | return err | |
442 | } | |
443 | ||
444 | // Do the walk | |
445 | if _, err := c.walk(graph, nil, walkInput); err != nil { | |
446 | return err | |
447 | } | |
448 | } | |
449 | ||
450 | return nil | |
451 | } | |
452 | ||
453 | // Apply applies the changes represented by this context and returns | |
454 | // the resulting state. | |
455 | // | |
456 | // Even in the case an error is returned, the state may be returned and will | |
457 | // potentially be partially updated. In addition to returning the resulting | |
458 | // state, this context is updated with the latest state. | |
459 | // | |
460 | // If the state is required after an error, the caller should call | |
461 | // Context.State, rather than rely on the return value. | |
462 | // | |
463 | // TODO: Apply and Refresh should either always return a state, or rely on the | |
464 | // State() method. Currently the helper/resource testing framework relies | |
465 | // on the absence of a returned state to determine if Destroy can be | |
466 | // called, so that will need to be refactored before this can be changed. | |
467 | func (c *Context) Apply() (*State, error) { | |
468 | defer c.acquireRun("apply")() | |
469 | ||
470 | // Copy our own state | |
471 | c.state = c.state.DeepCopy() | |
472 | ||
473 | // Build the graph. | |
474 | graph, err := c.Graph(GraphTypeApply, nil) | |
475 | if err != nil { | |
476 | return nil, err | |
477 | } | |
478 | ||
479 | // Determine the operation | |
480 | operation := walkApply | |
481 | if c.destroy { | |
482 | operation = walkDestroy | |
483 | } | |
484 | ||
485 | // Walk the graph | |
486 | walker, err := c.walk(graph, graph, operation) | |
487 | if len(walker.ValidationErrors) > 0 { | |
488 | err = multierror.Append(err, walker.ValidationErrors...) | |
489 | } | |
490 | ||
491 | // Clean out any unused things | |
492 | c.state.prune() | |
493 | ||
494 | return c.state, err | |
495 | } | |
496 | ||
497 | // Plan generates an execution plan for the given context. | |
498 | // | |
499 | // The execution plan encapsulates the context and can be stored | |
500 | // in order to reinstantiate a context later for Apply. | |
501 | // | |
502 | // Plan also updates the diff of this context to be the diff generated | |
503 | // by the plan, so Apply can be called after. | |
504 | func (c *Context) Plan() (*Plan, error) { | |
505 | defer c.acquireRun("plan")() | |
506 | ||
507 | p := &Plan{ | |
508 | Module: c.module, | |
509 | Vars: c.variables, | |
510 | State: c.state, | |
511 | Targets: c.targets, | |
512 | } | |
513 | ||
514 | var operation walkOperation | |
515 | if c.destroy { | |
516 | operation = walkPlanDestroy | |
517 | } else { | |
518 | // Set our state to be something temporary. We do this so that | |
519 | // the plan can update a fake state so that variables work, then | |
520 | // we replace it back with our old state. | |
521 | old := c.state | |
522 | if old == nil { | |
523 | c.state = &State{} | |
524 | c.state.init() | |
525 | } else { | |
526 | c.state = old.DeepCopy() | |
527 | } | |
528 | defer func() { | |
529 | c.state = old | |
530 | }() | |
531 | ||
532 | operation = walkPlan | |
533 | } | |
534 | ||
535 | // Setup our diff | |
536 | c.diffLock.Lock() | |
537 | c.diff = new(Diff) | |
538 | c.diff.init() | |
539 | c.diffLock.Unlock() | |
540 | ||
541 | // Build the graph. | |
542 | graphType := GraphTypePlan | |
543 | if c.destroy { | |
544 | graphType = GraphTypePlanDestroy | |
545 | } | |
546 | graph, err := c.Graph(graphType, nil) | |
547 | if err != nil { | |
548 | return nil, err | |
549 | } | |
550 | ||
551 | // Do the walk | |
552 | walker, err := c.walk(graph, graph, operation) | |
553 | if err != nil { | |
554 | return nil, err | |
555 | } | |
556 | p.Diff = c.diff | |
557 | ||
558 | // If this is true, it means we're running unit tests. In this case, | |
559 | // we perform a deep copy just to ensure that all context tests also | |
560 | // test that a diff is copy-able. This will panic if it fails. This | |
561 | // is enabled during unit tests. | |
562 | // | |
563 | // This should never be true during production usage, but even if it is, | |
564 | // it can't do any real harm. | |
565 | if contextTestDeepCopyOnPlan { | |
566 | p.Diff.DeepCopy() | |
567 | } | |
568 | ||
569 | /* | |
570 | // We don't do the reverification during the new destroy plan because | |
571 | // it will use a different apply process. | |
572 | if X_legacyGraph { | |
573 | // Now that we have a diff, we can build the exact graph that Apply will use | |
574 | // and catch any possible cycles during the Plan phase. | |
575 | if _, err := c.Graph(GraphTypeLegacy, nil); err != nil { | |
576 | return nil, err | |
577 | } | |
578 | } | |
579 | */ | |
580 | ||
581 | var errs error | |
582 | if len(walker.ValidationErrors) > 0 { | |
583 | errs = multierror.Append(errs, walker.ValidationErrors...) | |
584 | } | |
585 | return p, errs | |
586 | } | |
587 | ||
588 | // Refresh goes through all the resources in the state and refreshes them | |
589 | // to their latest state. This will update the state that this context | |
590 | // works with, along with returning it. | |
591 | // | |
592 | // Even in the case an error is returned, the state may be returned and | |
593 | // will potentially be partially updated. | |
594 | func (c *Context) Refresh() (*State, error) { | |
595 | defer c.acquireRun("refresh")() | |
596 | ||
597 | // Copy our own state | |
598 | c.state = c.state.DeepCopy() | |
599 | ||
600 | // Build the graph. | |
601 | graph, err := c.Graph(GraphTypeRefresh, nil) | |
602 | if err != nil { | |
603 | return nil, err | |
604 | } | |
605 | ||
606 | // Do the walk | |
607 | if _, err := c.walk(graph, graph, walkRefresh); err != nil { | |
608 | return nil, err | |
609 | } | |
610 | ||
611 | // Clean out any unused things | |
612 | c.state.prune() | |
613 | ||
614 | return c.state, nil | |
615 | } | |
616 | ||
617 | // Stop stops the running task. | |
618 | // | |
619 | // Stop will block until the task completes. | |
620 | func (c *Context) Stop() { | |
621 | log.Printf("[WARN] terraform: Stop called, initiating interrupt sequence") | |
622 | ||
623 | c.l.Lock() | |
624 | defer c.l.Unlock() | |
625 | ||
626 | // If we're running, then stop | |
627 | if c.runContextCancel != nil { | |
628 | log.Printf("[WARN] terraform: run context exists, stopping") | |
629 | ||
630 | // Tell the hook we want to stop | |
631 | c.sh.Stop() | |
632 | ||
633 | // Stop the context | |
634 | c.runContextCancel() | |
635 | c.runContextCancel = nil | |
636 | } | |
637 | ||
638 | // Grab the condition var before we exit | |
639 | if cond := c.runCond; cond != nil { | |
640 | cond.Wait() | |
641 | } | |
642 | ||
643 | log.Printf("[WARN] terraform: stop complete") | |
644 | } | |
645 | ||
646 | // Validate validates the configuration and returns any warnings or errors. | |
647 | func (c *Context) Validate() ([]string, []error) { | |
648 | defer c.acquireRun("validate")() | |
649 | ||
650 | var errs error | |
651 | ||
652 | // Validate the configuration itself | |
653 | if err := c.module.Validate(); err != nil { | |
654 | errs = multierror.Append(errs, err) | |
655 | } | |
656 | ||
657 | // This only needs to be done for the root module, since inter-module | |
658 | // variables are validated in the module tree. | |
659 | if config := c.module.Config(); config != nil { | |
660 | // Validate the user variables | |
661 | if err := smcUserVariables(config, c.variables); len(err) > 0 { | |
662 | errs = multierror.Append(errs, err...) | |
663 | } | |
664 | } | |
665 | ||
666 | // If we have errors at this point, the graphing has no chance, | |
667 | // so just bail early. | |
668 | if errs != nil { | |
669 | return nil, []error{errs} | |
670 | } | |
671 | ||
672 | // Build the graph so we can walk it and run Validate on nodes. | |
673 | // We also validate the graph generated here, but this graph doesn't | |
674 | // necessarily match the graph that Plan will generate, so we'll validate the | |
675 | // graph again later after Planning. | |
676 | graph, err := c.Graph(GraphTypeValidate, nil) | |
677 | if err != nil { | |
678 | return nil, []error{err} | |
679 | } | |
680 | ||
681 | // Walk | |
682 | walker, err := c.walk(graph, graph, walkValidate) | |
683 | if err != nil { | |
684 | return nil, multierror.Append(errs, err).Errors | |
685 | } | |
686 | ||
687 | // Return the result | |
688 | rerrs := multierror.Append(errs, walker.ValidationErrors...) | |
689 | ||
690 | sort.Strings(walker.ValidationWarnings) | |
691 | sort.Slice(rerrs.Errors, func(i, j int) bool { | |
692 | return rerrs.Errors[i].Error() < rerrs.Errors[j].Error() | |
693 | }) | |
694 | ||
695 | return walker.ValidationWarnings, rerrs.Errors | |
696 | } | |
697 | ||
698 | // Module returns the module tree associated with this context. | |
699 | func (c *Context) Module() *module.Tree { | |
700 | return c.module | |
701 | } | |
702 | ||
703 | // Variables will return the mapping of variables that were defined | |
704 | // for this Context. If Input was called, this mapping may be different | |
705 | // than what was given. | |
706 | func (c *Context) Variables() map[string]interface{} { | |
707 | return c.variables | |
708 | } | |
709 | ||
710 | // SetVariable sets a variable after a context has already been built. | |
711 | func (c *Context) SetVariable(k string, v interface{}) { | |
712 | c.variables[k] = v | |
713 | } | |
714 | ||
715 | func (c *Context) acquireRun(phase string) func() { | |
716 | // With the run lock held, grab the context lock to make changes | |
717 | // to the run context. | |
718 | c.l.Lock() | |
719 | defer c.l.Unlock() | |
720 | ||
721 | // Wait until we're no longer running | |
722 | for c.runCond != nil { | |
723 | c.runCond.Wait() | |
724 | } | |
725 | ||
726 | // Build our lock | |
727 | c.runCond = sync.NewCond(&c.l) | |
728 | ||
729 | // Setup debugging | |
730 | dbug.SetPhase(phase) | |
731 | ||
732 | // Create a new run context | |
733 | c.runContext, c.runContextCancel = context.WithCancel(context.Background()) | |
734 | ||
735 | // Reset the stop hook so we're not stopped | |
736 | c.sh.Reset() | |
737 | ||
738 | // Reset the shadow errors | |
739 | c.shadowErr = nil | |
740 | ||
741 | return c.releaseRun | |
742 | } | |
743 | ||
744 | func (c *Context) releaseRun() { | |
745 | // Grab the context lock so that we can make modifications to fields | |
746 | c.l.Lock() | |
747 | defer c.l.Unlock() | |
748 | ||
749 | // setting the phase to "INVALID" lets us easily detect if we have | |
750 | // operations happening outside of a run, or we missed setting the proper | |
751 | // phase | |
752 | dbug.SetPhase("INVALID") | |
753 | ||
754 | // End our run. We check if runContext is non-nil because it can be | |
755 | // set to nil if it was cancelled via Stop() | |
756 | if c.runContextCancel != nil { | |
757 | c.runContextCancel() | |
758 | } | |
759 | ||
760 | // Unlock all waiting our condition | |
761 | cond := c.runCond | |
762 | c.runCond = nil | |
763 | cond.Broadcast() | |
764 | ||
765 | // Unset the context | |
766 | c.runContext = nil | |
767 | } | |
768 | ||
769 | func (c *Context) walk( | |
770 | graph, shadow *Graph, operation walkOperation) (*ContextGraphWalker, error) { | |
771 | // Keep track of the "real" context which is the context that does | |
772 | // the real work: talking to real providers, modifying real state, etc. | |
773 | realCtx := c | |
774 | ||
775 | // If we don't want shadowing, remove it | |
776 | if !experiment.Enabled(experiment.X_shadow) { | |
777 | shadow = nil | |
778 | } | |
779 | ||
780 | // Just log this so we can see it in a debug log | |
781 | if !c.shadow { | |
782 | log.Printf("[WARN] terraform: shadow graph disabled") | |
783 | shadow = nil | |
784 | } | |
785 | ||
786 | // If we have a shadow graph, walk that as well | |
787 | var shadowCtx *Context | |
788 | var shadowCloser Shadow | |
789 | if shadow != nil { | |
790 | // Build the shadow context. In the process, override the real context | |
791 | // with the one that is wrapped so that the shadow context can verify | |
792 | // the results of the real. | |
793 | realCtx, shadowCtx, shadowCloser = newShadowContext(c) | |
794 | } | |
795 | ||
796 | log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) | |
797 | ||
798 | walker := &ContextGraphWalker{ | |
799 | Context: realCtx, | |
800 | Operation: operation, | |
801 | StopContext: c.runContext, | |
802 | } | |
803 | ||
804 | // Watch for a stop so we can call the provider Stop() API. | |
805 | watchStop, watchWait := c.watchStop(walker) | |
806 | ||
807 | // Walk the real graph, this will block until it completes | |
808 | realErr := graph.Walk(walker) | |
809 | ||
810 | // Close the channel so the watcher stops, and wait for it to return. | |
811 | close(watchStop) | |
812 | <-watchWait | |
813 | ||
814 | // If we have a shadow graph and we interrupted the real graph, then | |
815 | // we just close the shadow and never verify it. It is non-trivial to | |
816 | // recreate the exact execution state up until an interruption so this | |
817 | // isn't supported with shadows at the moment. | |
818 | if shadowCloser != nil && c.sh.Stopped() { | |
819 | // Ignore the error result, there is nothing we could care about | |
820 | shadowCloser.CloseShadow() | |
821 | ||
822 | // Set it to nil so we don't do anything | |
823 | shadowCloser = nil | |
824 | } | |
825 | ||
826 | // If we have a shadow graph, wait for that to complete. | |
827 | if shadowCloser != nil { | |
828 | // Build the graph walker for the shadow. We also wrap this in | |
829 | // a panicwrap so that panics are captured. For the shadow graph, | |
830 | // we just want panics to be normal errors rather than to crash | |
831 | // Terraform. | |
832 | shadowWalker := GraphWalkerPanicwrap(&ContextGraphWalker{ | |
833 | Context: shadowCtx, | |
834 | Operation: operation, | |
835 | }) | |
836 | ||
837 | // Kick off the shadow walk. This will block on any operations | |
838 | // on the real walk so it is fine to start first. | |
839 | log.Printf("[INFO] Starting shadow graph walk: %s", operation.String()) | |
840 | shadowCh := make(chan error) | |
841 | go func() { | |
842 | shadowCh <- shadow.Walk(shadowWalker) | |
843 | }() | |
844 | ||
845 | // Notify the shadow that we're done | |
846 | if err := shadowCloser.CloseShadow(); err != nil { | |
847 | c.shadowErr = multierror.Append(c.shadowErr, err) | |
848 | } | |
849 | ||
850 | // Wait for the walk to end | |
851 | log.Printf("[DEBUG] Waiting for shadow graph to complete...") | |
852 | shadowWalkErr := <-shadowCh | |
853 | ||
854 | // Get any shadow errors | |
855 | if err := shadowCloser.ShadowError(); err != nil { | |
856 | c.shadowErr = multierror.Append(c.shadowErr, err) | |
857 | } | |
858 | ||
859 | // Verify the contexts (compare) | |
860 | if err := shadowContextVerify(realCtx, shadowCtx); err != nil { | |
861 | c.shadowErr = multierror.Append(c.shadowErr, err) | |
862 | } | |
863 | ||
864 | // At this point, if we're supposed to fail on error, then | |
865 | // we PANIC. Some tests just verify that there is an error, | |
866 | // so simply appending it to realErr and returning could hide | |
867 | // shadow problems. | |
868 | // | |
869 | // This must be done BEFORE appending shadowWalkErr since the | |
870 | // shadowWalkErr may include expected errors. | |
871 | // | |
872 | // We only do this if we don't have a real error. In the case of | |
873 | // a real error, we can't guarantee what nodes were and weren't | |
874 | // traversed in parallel scenarios so we can't guarantee no | |
875 | // shadow errors. | |
876 | if c.shadowErr != nil && contextFailOnShadowError && realErr == nil { | |
877 | panic(multierror.Prefix(c.shadowErr, "shadow graph:")) | |
878 | } | |
879 | ||
880 | // Now, if we have a walk error, we append that through | |
881 | if shadowWalkErr != nil { | |
882 | c.shadowErr = multierror.Append(c.shadowErr, shadowWalkErr) | |
883 | } | |
884 | ||
885 | if c.shadowErr == nil { | |
886 | log.Printf("[INFO] Shadow graph success!") | |
887 | } else { | |
888 | log.Printf("[ERROR] Shadow graph error: %s", c.shadowErr) | |
889 | ||
890 | // If we're supposed to fail on shadow errors, then report it | |
891 | if contextFailOnShadowError { | |
892 | realErr = multierror.Append(realErr, multierror.Prefix( | |
893 | c.shadowErr, "shadow graph:")) | |
894 | } | |
895 | } | |
896 | } | |
897 | ||
898 | return walker, realErr | |
899 | } | |
900 | ||
901 | // watchStop immediately returns a `stop` and a `wait` chan after dispatching | |
902 | // the watchStop goroutine. This will watch the runContext for cancellation and | |
903 | // stop the providers accordingly. When the watch is no longer needed, the | |
904 | // `stop` chan should be closed before waiting on the `wait` chan. | |
905 | // The `wait` chan is important, because without synchronizing with the end of | |
906 | // the watchStop goroutine, the runContext may also be closed during the select | |
907 | // incorrectly causing providers to be stopped. Even if the graph walk is done | |
908 | // at that point, stopping a provider permanently cancels its StopContext which | |
909 | // can cause later actions to fail. | |
910 | func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan struct{}) { | |
911 | stop := make(chan struct{}) | |
912 | wait := make(chan struct{}) | |
913 | ||
914 | // get the runContext cancellation channel now, because releaseRun will | |
915 | // write to the runContext field. | |
916 | done := c.runContext.Done() | |
917 | ||
918 | go func() { | |
919 | defer close(wait) | |
920 | // Wait for a stop or completion | |
921 | select { | |
922 | case <-done: | |
923 | // done means the context was canceled, so we need to try and stop | |
924 | // providers. | |
925 | case <-stop: | |
926 | // our own stop channel was closed. | |
927 | return | |
928 | } | |
929 | ||
930 | // If we're here, we're stopped, trigger the call. | |
931 | ||
932 | { | |
933 | // Copy the providers so that a misbehaved blocking Stop doesn't | |
934 | // completely hang Terraform. | |
935 | walker.providerLock.Lock() | |
936 | ps := make([]ResourceProvider, 0, len(walker.providerCache)) | |
937 | for _, p := range walker.providerCache { | |
938 | ps = append(ps, p) | |
939 | } | |
940 | defer walker.providerLock.Unlock() | |
941 | ||
942 | for _, p := range ps { | |
943 | // We ignore the error for now since there isn't any reasonable | |
944 | // action to take if there is an error here, since the stop is still | |
945 | // advisory: Terraform will exit once the graph node completes. | |
946 | p.Stop() | |
947 | } | |
948 | } | |
949 | ||
950 | { | |
951 | // Call stop on all the provisioners | |
952 | walker.provisionerLock.Lock() | |
953 | ps := make([]ResourceProvisioner, 0, len(walker.provisionerCache)) | |
954 | for _, p := range walker.provisionerCache { | |
955 | ps = append(ps, p) | |
956 | } | |
957 | defer walker.provisionerLock.Unlock() | |
958 | ||
959 | for _, p := range ps { | |
960 | // We ignore the error for now since there isn't any reasonable | |
961 | // action to take if there is an error here, since the stop is still | |
962 | // advisory: Terraform will exit once the graph node completes. | |
963 | p.Stop() | |
964 | } | |
965 | } | |
966 | }() | |
967 | ||
968 | return stop, wait | |
969 | } | |
970 | ||
971 | // parseVariableAsHCL parses the value of a single variable as would have been specified | |
972 | // on the command line via -var or in an environment variable named TF_VAR_x, where x is | |
973 | // the name of the variable. In order to get around the restriction of HCL requiring a | |
974 | // top level object, we prepend a sentinel key, decode the user-specified value as its | |
975 | // value and pull the value back out of the resulting map. | |
976 | func parseVariableAsHCL(name string, input string, targetType config.VariableType) (interface{}, error) { | |
977 | // expecting a string so don't decode anything, just strip quotes | |
978 | if targetType == config.VariableTypeString { | |
979 | return strings.Trim(input, `"`), nil | |
980 | } | |
981 | ||
982 | // return empty types | |
983 | if strings.TrimSpace(input) == "" { | |
984 | switch targetType { | |
985 | case config.VariableTypeList: | |
986 | return []interface{}{}, nil | |
987 | case config.VariableTypeMap: | |
988 | return make(map[string]interface{}), nil | |
989 | } | |
990 | } | |
991 | ||
992 | const sentinelValue = "SENTINEL_TERRAFORM_VAR_OVERRIDE_KEY" | |
993 | inputWithSentinal := fmt.Sprintf("%s = %s", sentinelValue, input) | |
994 | ||
995 | var decoded map[string]interface{} | |
996 | err := hcl.Decode(&decoded, inputWithSentinal) | |
997 | if err != nil { | |
998 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL: %s", name, input, err) | |
999 | } | |
1000 | ||
1001 | if len(decoded) != 1 { | |
1002 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. Only one value may be specified.", name, input) | |
1003 | } | |
1004 | ||
1005 | parsedValue, ok := decoded[sentinelValue] | |
1006 | if !ok { | |
1007 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) | |
1008 | } | |
1009 | ||
1010 | switch targetType { | |
1011 | case config.VariableTypeList: | |
1012 | return parsedValue, nil | |
1013 | case config.VariableTypeMap: | |
1014 | if list, ok := parsedValue.([]map[string]interface{}); ok { | |
1015 | return list[0], nil | |
1016 | } | |
1017 | ||
1018 | return nil, fmt.Errorf("Cannot parse value for variable %s (%q) as valid HCL. One value must be specified.", name, input) | |
1019 | default: | |
1020 | panic(fmt.Errorf("unknown type %s", targetType.Printable())) | |
1021 | } | |
1022 | } |