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