diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/module/tree.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/module/tree.go | 314 |
1 files changed, 233 insertions, 81 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree.go b/vendor/github.com/hashicorp/terraform/config/module/tree.go index 4b0b153..f56d69b 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/tree.go +++ b/vendor/github.com/hashicorp/terraform/config/module/tree.go | |||
@@ -4,11 +4,14 @@ import ( | |||
4 | "bufio" | 4 | "bufio" |
5 | "bytes" | 5 | "bytes" |
6 | "fmt" | 6 | "fmt" |
7 | "log" | ||
7 | "path/filepath" | 8 | "path/filepath" |
8 | "strings" | 9 | "strings" |
9 | "sync" | 10 | "sync" |
10 | 11 | ||
11 | "github.com/hashicorp/go-getter" | 12 | "github.com/hashicorp/terraform/tfdiags" |
13 | |||
14 | getter "github.com/hashicorp/go-getter" | ||
12 | "github.com/hashicorp/terraform/config" | 15 | "github.com/hashicorp/terraform/config" |
13 | ) | 16 | ) |
14 | 17 | ||
@@ -26,6 +29,17 @@ type Tree struct { | |||
26 | children map[string]*Tree | 29 | children map[string]*Tree |
27 | path []string | 30 | path []string |
28 | lock sync.RWMutex | 31 | lock sync.RWMutex |
32 | |||
33 | // version is the final version of the config loaded for the Tree's module | ||
34 | version string | ||
35 | // source is the "source" string used to load this module. It's possible | ||
36 | // for a module source to change, but the path remains the same, preventing | ||
37 | // it from being reloaded. | ||
38 | source string | ||
39 | // parent allows us to walk back up the tree and determine if there are any | ||
40 | // versioned ancestor modules which may effect the stored location of | ||
41 | // submodules | ||
42 | parent *Tree | ||
29 | } | 43 | } |
30 | 44 | ||
31 | // NewTree returns a new Tree for the given config structure. | 45 | // NewTree returns a new Tree for the given config structure. |
@@ -40,7 +54,7 @@ func NewEmptyTree() *Tree { | |||
40 | // We do this dummy load so that the tree is marked as "loaded". It | 54 | // We do this dummy load so that the tree is marked as "loaded". It |
41 | // should never fail because this is just about a no-op. If it does fail | 55 | // should never fail because this is just about a no-op. If it does fail |
42 | // we panic so we can know its a bug. | 56 | // we panic so we can know its a bug. |
43 | if err := t.Load(nil, GetModeGet); err != nil { | 57 | if err := t.Load(&Storage{Mode: GetModeGet}); err != nil { |
44 | panic(err) | 58 | panic(err) |
45 | } | 59 | } |
46 | 60 | ||
@@ -126,8 +140,10 @@ func (t *Tree) Modules() []*Module { | |||
126 | result := make([]*Module, len(t.config.Modules)) | 140 | result := make([]*Module, len(t.config.Modules)) |
127 | for i, m := range t.config.Modules { | 141 | for i, m := range t.config.Modules { |
128 | result[i] = &Module{ | 142 | result[i] = &Module{ |
129 | Name: m.Name, | 143 | Name: m.Name, |
130 | Source: m.Source, | 144 | Version: m.Version, |
145 | Source: m.Source, | ||
146 | Providers: m.Providers, | ||
131 | } | 147 | } |
132 | } | 148 | } |
133 | 149 | ||
@@ -155,81 +171,178 @@ func (t *Tree) Name() string { | |||
155 | // module trees inherently require the configuration to be in a reasonably | 171 | // module trees inherently require the configuration to be in a reasonably |
156 | // sane state: no circular dependencies, proper module sources, etc. A full | 172 | // sane state: no circular dependencies, proper module sources, etc. A full |
157 | // suite of validations can be done by running Validate (after loading). | 173 | // suite of validations can be done by running Validate (after loading). |
158 | func (t *Tree) Load(s getter.Storage, mode GetMode) error { | 174 | func (t *Tree) Load(s *Storage) error { |
159 | t.lock.Lock() | 175 | t.lock.Lock() |
160 | defer t.lock.Unlock() | 176 | defer t.lock.Unlock() |
161 | 177 | ||
162 | // Reset the children if we have any | 178 | children, err := t.getChildren(s) |
163 | t.children = nil | 179 | if err != nil { |
180 | return err | ||
181 | } | ||
182 | |||
183 | // Go through all the children and load them. | ||
184 | for _, c := range children { | ||
185 | if err := c.Load(s); err != nil { | ||
186 | return err | ||
187 | } | ||
188 | } | ||
189 | |||
190 | // Set our tree up | ||
191 | t.children = children | ||
164 | 192 | ||
165 | modules := t.Modules() | 193 | return nil |
194 | } | ||
195 | |||
196 | func (t *Tree) getChildren(s *Storage) (map[string]*Tree, error) { | ||
166 | children := make(map[string]*Tree) | 197 | children := make(map[string]*Tree) |
167 | 198 | ||
168 | // Go through all the modules and get the directory for them. | 199 | // Go through all the modules and get the directory for them. |
169 | for _, m := range modules { | 200 | for _, m := range t.Modules() { |
170 | if _, ok := children[m.Name]; ok { | 201 | if _, ok := children[m.Name]; ok { |
171 | return fmt.Errorf( | 202 | return nil, fmt.Errorf( |
172 | "module %s: duplicated. module names must be unique", m.Name) | 203 | "module %s: duplicated. module names must be unique", m.Name) |
173 | } | 204 | } |
174 | 205 | ||
175 | // Determine the path to this child | 206 | // Determine the path to this child |
176 | path := make([]string, len(t.path), len(t.path)+1) | 207 | modPath := make([]string, len(t.path), len(t.path)+1) |
177 | copy(path, t.path) | 208 | copy(modPath, t.path) |
178 | path = append(path, m.Name) | 209 | modPath = append(modPath, m.Name) |
179 | 210 | ||
180 | // Split out the subdir if we have one | 211 | log.Printf("[TRACE] module source: %q", m.Source) |
181 | source, subDir := getter.SourceDirSubdir(m.Source) | ||
182 | 212 | ||
183 | source, err := getter.Detect(source, t.config.Dir, getter.Detectors) | 213 | // add the module path to help indicate where modules with relative |
214 | // paths are being loaded from | ||
215 | s.output(fmt.Sprintf("- module.%s", strings.Join(modPath, "."))) | ||
216 | |||
217 | // Lookup the local location of the module. | ||
218 | // dir is the local directory where the module is stored | ||
219 | mod, err := s.findRegistryModule(m.Source, m.Version) | ||
184 | if err != nil { | 220 | if err != nil { |
185 | return fmt.Errorf("module %s: %s", m.Name, err) | 221 | return nil, err |
186 | } | 222 | } |
187 | 223 | ||
224 | // The key is the string that will be used to uniquely id the Source in | ||
225 | // the local storage. The prefix digit can be incremented to | ||
226 | // invalidate the local module storage. | ||
227 | key := "1." + t.versionedPathKey(m) | ||
228 | if mod.Version != "" { | ||
229 | key += "." + mod.Version | ||
230 | } | ||
231 | |||
232 | // Check for the exact key if it's not a registry module | ||
233 | if !mod.registry { | ||
234 | mod.Dir, err = s.findModule(key) | ||
235 | if err != nil { | ||
236 | return nil, err | ||
237 | } | ||
238 | } | ||
239 | |||
240 | if mod.Dir != "" && s.Mode != GetModeUpdate { | ||
241 | // We found it locally, but in order to load the Tree we need to | ||
242 | // find out if there was another subDir stored from detection. | ||
243 | subDir, err := s.getModuleRoot(mod.Dir) | ||
244 | if err != nil { | ||
245 | // If there's a problem with the subdir record, we'll let the | ||
246 | // recordSubdir method fix it up. Any other filesystem errors | ||
247 | // will turn up again below. | ||
248 | log.Println("[WARN] error reading subdir record:", err) | ||
249 | } | ||
250 | |||
251 | fullDir := filepath.Join(mod.Dir, subDir) | ||
252 | |||
253 | child, err := NewTreeModule(m.Name, fullDir) | ||
254 | if err != nil { | ||
255 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
256 | } | ||
257 | child.path = modPath | ||
258 | child.parent = t | ||
259 | child.version = mod.Version | ||
260 | child.source = m.Source | ||
261 | children[m.Name] = child | ||
262 | continue | ||
263 | } | ||
264 | |||
265 | // Split out the subdir if we have one. | ||
266 | // Terraform keeps the entire requested tree, so that modules can | ||
267 | // reference sibling modules from the same archive or repo. | ||
268 | rawSource, subDir := getter.SourceDirSubdir(m.Source) | ||
269 | |||
270 | // we haven't found a source, so fallback to the go-getter detectors | ||
271 | source := mod.url | ||
272 | if source == "" { | ||
273 | source, err = getter.Detect(rawSource, t.config.Dir, getter.Detectors) | ||
274 | if err != nil { | ||
275 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
276 | } | ||
277 | } | ||
278 | |||
279 | log.Printf("[TRACE] detected module source %q", source) | ||
280 | |||
188 | // Check if the detector introduced something new. | 281 | // Check if the detector introduced something new. |
189 | source, subDir2 := getter.SourceDirSubdir(source) | 282 | // For example, the registry always adds a subdir of `//*`, |
190 | if subDir2 != "" { | 283 | // indicating that we need to strip off the first component from the |
191 | subDir = filepath.Join(subDir2, subDir) | 284 | // tar archive, though we may not yet know what it is called. |
285 | source, detectedSubDir := getter.SourceDirSubdir(source) | ||
286 | if detectedSubDir != "" { | ||
287 | subDir = filepath.Join(detectedSubDir, subDir) | ||
288 | } | ||
289 | |||
290 | output := "" | ||
291 | switch s.Mode { | ||
292 | case GetModeUpdate: | ||
293 | output = fmt.Sprintf(" Updating source %q", m.Source) | ||
294 | default: | ||
295 | output = fmt.Sprintf(" Getting source %q", m.Source) | ||
192 | } | 296 | } |
297 | s.output(output) | ||
193 | 298 | ||
194 | // Get the directory where this module is so we can load it | 299 | dir, ok, err := s.getStorage(key, source) |
195 | key := strings.Join(path, ".") | ||
196 | key = fmt.Sprintf("root.%s-%s", key, m.Source) | ||
197 | dir, ok, err := getStorage(s, key, source, mode) | ||
198 | if err != nil { | 300 | if err != nil { |
199 | return err | 301 | return nil, err |
200 | } | 302 | } |
201 | if !ok { | 303 | if !ok { |
202 | return fmt.Errorf( | 304 | return nil, fmt.Errorf("module %s: not found, may need to run 'terraform init'", m.Name) |
203 | "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) | ||
204 | } | 305 | } |
205 | 306 | ||
206 | // If we have a subdirectory, then merge that in | 307 | log.Printf("[TRACE] %q stored in %q", source, dir) |
308 | |||
309 | // expand and record the subDir for later | ||
310 | fullDir := dir | ||
207 | if subDir != "" { | 311 | if subDir != "" { |
208 | dir = filepath.Join(dir, subDir) | 312 | fullDir, err = getter.SubdirGlob(dir, subDir) |
209 | } | 313 | if err != nil { |
314 | return nil, err | ||
315 | } | ||
210 | 316 | ||
211 | // Load the configurations.Dir(source) | 317 | // +1 to account for the pathsep |
212 | children[m.Name], err = NewTreeModule(m.Name, dir) | 318 | if len(dir)+1 > len(fullDir) { |
213 | if err != nil { | 319 | return nil, fmt.Errorf("invalid module storage path %q", fullDir) |
214 | return fmt.Errorf( | 320 | } |
215 | "module %s: %s", m.Name, err) | 321 | subDir = fullDir[len(dir)+1:] |
216 | } | 322 | } |
217 | 323 | ||
218 | // Set the path of this child | 324 | // add new info to the module record |
219 | children[m.Name].path = path | 325 | mod.Key = key |
220 | } | 326 | mod.Dir = dir |
327 | mod.Root = subDir | ||
221 | 328 | ||
222 | // Go through all the children and load them. | 329 | // record the module in our manifest |
223 | for _, c := range children { | 330 | if err := s.recordModule(mod); err != nil { |
224 | if err := c.Load(s, mode); err != nil { | 331 | return nil, err |
225 | return err | ||
226 | } | 332 | } |
227 | } | ||
228 | 333 | ||
229 | // Set our tree up | 334 | child, err := NewTreeModule(m.Name, fullDir) |
230 | t.children = children | 335 | if err != nil { |
336 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
337 | } | ||
338 | child.path = modPath | ||
339 | child.parent = t | ||
340 | child.version = mod.Version | ||
341 | child.source = m.Source | ||
342 | children[m.Name] = child | ||
343 | } | ||
231 | 344 | ||
232 | return nil | 345 | return children, nil |
233 | } | 346 | } |
234 | 347 | ||
235 | // Path is the full path to this tree. | 348 | // Path is the full path to this tree. |
@@ -272,32 +385,35 @@ func (t *Tree) String() string { | |||
272 | // as verifying things such as parameters/outputs between the various modules. | 385 | // as verifying things such as parameters/outputs between the various modules. |
273 | // | 386 | // |
274 | // Load must be called prior to calling Validate or an error will be returned. | 387 | // Load must be called prior to calling Validate or an error will be returned. |
275 | func (t *Tree) Validate() error { | 388 | func (t *Tree) Validate() tfdiags.Diagnostics { |
389 | var diags tfdiags.Diagnostics | ||
390 | |||
276 | if !t.Loaded() { | 391 | if !t.Loaded() { |
277 | return fmt.Errorf("tree must be loaded before calling Validate") | 392 | diags = diags.Append(fmt.Errorf( |
393 | "tree must be loaded before calling Validate", | ||
394 | )) | ||
395 | return diags | ||
278 | } | 396 | } |
279 | 397 | ||
280 | // If something goes wrong, here is our error template | ||
281 | newErr := &treeError{Name: []string{t.Name()}} | ||
282 | |||
283 | // Terraform core does not handle root module children named "root". | 398 | // Terraform core does not handle root module children named "root". |
284 | // We plan to fix this in the future but this bug was brought up in | 399 | // We plan to fix this in the future but this bug was brought up in |
285 | // the middle of a release and we don't want to introduce wide-sweeping | 400 | // the middle of a release and we don't want to introduce wide-sweeping |
286 | // changes at that time. | 401 | // changes at that time. |
287 | if len(t.path) == 1 && t.name == "root" { | 402 | if len(t.path) == 1 && t.name == "root" { |
288 | return fmt.Errorf("root module cannot contain module named 'root'") | 403 | diags = diags.Append(fmt.Errorf( |
404 | "root module cannot contain module named 'root'", | ||
405 | )) | ||
406 | return diags | ||
289 | } | 407 | } |
290 | 408 | ||
291 | // Validate our configuration first. | 409 | // Validate our configuration first. |
292 | if err := t.config.Validate(); err != nil { | 410 | diags = diags.Append(t.config.Validate()) |
293 | newErr.Add(err) | ||
294 | } | ||
295 | 411 | ||
296 | // If we're the root, we do extra validation. This validation usually | 412 | // If we're the root, we do extra validation. This validation usually |
297 | // requires the entire tree (since children don't have parent pointers). | 413 | // requires the entire tree (since children don't have parent pointers). |
298 | if len(t.path) == 0 { | 414 | if len(t.path) == 0 { |
299 | if err := t.validateProviderAlias(); err != nil { | 415 | if err := t.validateProviderAlias(); err != nil { |
300 | newErr.Add(err) | 416 | diags = diags.Append(err) |
301 | } | 417 | } |
302 | } | 418 | } |
303 | 419 | ||
@@ -306,20 +422,11 @@ func (t *Tree) Validate() error { | |||
306 | 422 | ||
307 | // Validate all our children | 423 | // Validate all our children |
308 | for _, c := range children { | 424 | for _, c := range children { |
309 | err := c.Validate() | 425 | childDiags := c.Validate() |
310 | if err == nil { | 426 | diags = diags.Append(childDiags) |
427 | if diags.HasErrors() { | ||
311 | continue | 428 | continue |
312 | } | 429 | } |
313 | |||
314 | verr, ok := err.(*treeError) | ||
315 | if !ok { | ||
316 | // Unknown error, just return... | ||
317 | return err | ||
318 | } | ||
319 | |||
320 | // Append ourselves to the error and then return | ||
321 | verr.Name = append(verr.Name, t.Name()) | ||
322 | newErr.AddChild(verr) | ||
323 | } | 430 | } |
324 | 431 | ||
325 | // Go over all the modules and verify that any parameters are valid | 432 | // Go over all the modules and verify that any parameters are valid |
@@ -345,9 +452,10 @@ func (t *Tree) Validate() error { | |||
345 | // Compare to the keys in our raw config for the module | 452 | // Compare to the keys in our raw config for the module |
346 | for k, _ := range m.RawConfig.Raw { | 453 | for k, _ := range m.RawConfig.Raw { |
347 | if _, ok := varMap[k]; !ok { | 454 | if _, ok := varMap[k]; !ok { |
348 | newErr.Add(fmt.Errorf( | 455 | diags = diags.Append(fmt.Errorf( |
349 | "module %s: %s is not a valid parameter", | 456 | "module %q: %q is not a valid argument", |
350 | m.Name, k)) | 457 | m.Name, k, |
458 | )) | ||
351 | } | 459 | } |
352 | 460 | ||
353 | // Remove the required | 461 | // Remove the required |
@@ -356,9 +464,10 @@ func (t *Tree) Validate() error { | |||
356 | 464 | ||
357 | // If we have any required left over, they aren't set. | 465 | // If we have any required left over, they aren't set. |
358 | for k, _ := range requiredMap { | 466 | for k, _ := range requiredMap { |
359 | newErr.Add(fmt.Errorf( | 467 | diags = diags.Append(fmt.Errorf( |
360 | "module %s: required variable %q not set", | 468 | "module %q: missing required argument %q", |
361 | m.Name, k)) | 469 | m.Name, k, |
470 | )) | ||
362 | } | 471 | } |
363 | } | 472 | } |
364 | 473 | ||
@@ -373,9 +482,10 @@ func (t *Tree) Validate() error { | |||
373 | 482 | ||
374 | tree, ok := children[mv.Name] | 483 | tree, ok := children[mv.Name] |
375 | if !ok { | 484 | if !ok { |
376 | newErr.Add(fmt.Errorf( | 485 | diags = diags.Append(fmt.Errorf( |
377 | "%s: undefined module referenced %s", | 486 | "%s: reference to undefined module %q", |
378 | source, mv.Name)) | 487 | source, mv.Name, |
488 | )) | ||
379 | continue | 489 | continue |
380 | } | 490 | } |
381 | 491 | ||
@@ -387,14 +497,56 @@ func (t *Tree) Validate() error { | |||
387 | } | 497 | } |
388 | } | 498 | } |
389 | if !found { | 499 | if !found { |
390 | newErr.Add(fmt.Errorf( | 500 | diags = diags.Append(fmt.Errorf( |
391 | "%s: %s is not a valid output for module %s", | 501 | "%s: %q is not a valid output for module %q", |
392 | source, mv.Field, mv.Name)) | 502 | source, mv.Field, mv.Name, |
503 | )) | ||
393 | } | 504 | } |
394 | } | 505 | } |
395 | } | 506 | } |
396 | 507 | ||
397 | return newErr.ErrOrNil() | 508 | return diags |
509 | } | ||
510 | |||
511 | // versionedPathKey returns a path string with every levels full name, version | ||
512 | // and source encoded. This is to provide a unique key for our module storage, | ||
513 | // since submodules need to know which versions of their ancestor modules they | ||
514 | // are loaded from. | ||
515 | // For example, if module A has a subdirectory B, if module A's source or | ||
516 | // version is updated B's storage key must reflect this change in order for the | ||
517 | // correct version of B's source to be loaded. | ||
518 | func (t *Tree) versionedPathKey(m *Module) string { | ||
519 | path := make([]string, len(t.path)+1) | ||
520 | path[len(path)-1] = m.Name + ";" + m.Source | ||
521 | // We're going to load these in order for easier reading and debugging, but | ||
522 | // in practice they only need to be unique and consistent. | ||
523 | |||
524 | p := t | ||
525 | i := len(path) - 2 | ||
526 | for ; i >= 0; i-- { | ||
527 | if p == nil { | ||
528 | break | ||
529 | } | ||
530 | // we may have been loaded under a blank Tree, so always check for a name | ||
531 | // too. | ||
532 | if p.name == "" { | ||
533 | break | ||
534 | } | ||
535 | seg := p.name | ||
536 | if p.version != "" { | ||
537 | seg += "#" + p.version | ||
538 | } | ||
539 | |||
540 | if p.source != "" { | ||
541 | seg += ";" + p.source | ||
542 | } | ||
543 | |||
544 | path[i] = seg | ||
545 | p = p.parent | ||
546 | } | ||
547 | |||
548 | key := strings.Join(path, "|") | ||
549 | return key | ||
398 | } | 550 | } |
399 | 551 | ||
400 | // treeError is an error use by Tree.Validate to accumulates all | 552 | // treeError is an error use by Tree.Validate to accumulates all |