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 | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree.go b/vendor/github.com/hashicorp/terraform/config/module/tree.go new file mode 100644 index 0000000..b6f90fd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/tree.go | |||
@@ -0,0 +1,428 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "bytes" | ||
6 | "fmt" | ||
7 | "path/filepath" | ||
8 | "strings" | ||
9 | "sync" | ||
10 | |||
11 | "github.com/hashicorp/go-getter" | ||
12 | "github.com/hashicorp/terraform/config" | ||
13 | ) | ||
14 | |||
15 | // RootName is the name of the root tree. | ||
16 | const RootName = "root" | ||
17 | |||
18 | // Tree represents the module import tree of configurations. | ||
19 | // | ||
20 | // This Tree structure can be used to get (download) new modules, load | ||
21 | // all the modules without getting, flatten the tree into something | ||
22 | // Terraform can use, etc. | ||
23 | type Tree struct { | ||
24 | name string | ||
25 | config *config.Config | ||
26 | children map[string]*Tree | ||
27 | path []string | ||
28 | lock sync.RWMutex | ||
29 | } | ||
30 | |||
31 | // NewTree returns a new Tree for the given config structure. | ||
32 | func NewTree(name string, c *config.Config) *Tree { | ||
33 | return &Tree{config: c, name: name} | ||
34 | } | ||
35 | |||
36 | // NewEmptyTree returns a new tree that is empty (contains no configuration). | ||
37 | func NewEmptyTree() *Tree { | ||
38 | t := &Tree{config: &config.Config{}} | ||
39 | |||
40 | // 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 | ||
42 | // we panic so we can know its a bug. | ||
43 | if err := t.Load(nil, GetModeGet); err != nil { | ||
44 | panic(err) | ||
45 | } | ||
46 | |||
47 | return t | ||
48 | } | ||
49 | |||
50 | // NewTreeModule is like NewTree except it parses the configuration in | ||
51 | // the directory and gives it a specific name. Use a blank name "" to specify | ||
52 | // the root module. | ||
53 | func NewTreeModule(name, dir string) (*Tree, error) { | ||
54 | c, err := config.LoadDir(dir) | ||
55 | if err != nil { | ||
56 | return nil, err | ||
57 | } | ||
58 | |||
59 | return NewTree(name, c), nil | ||
60 | } | ||
61 | |||
62 | // Config returns the configuration for this module. | ||
63 | func (t *Tree) Config() *config.Config { | ||
64 | return t.config | ||
65 | } | ||
66 | |||
67 | // Child returns the child with the given path (by name). | ||
68 | func (t *Tree) Child(path []string) *Tree { | ||
69 | if t == nil { | ||
70 | return nil | ||
71 | } | ||
72 | |||
73 | if len(path) == 0 { | ||
74 | return t | ||
75 | } | ||
76 | |||
77 | c := t.Children()[path[0]] | ||
78 | if c == nil { | ||
79 | return nil | ||
80 | } | ||
81 | |||
82 | return c.Child(path[1:]) | ||
83 | } | ||
84 | |||
85 | // Children returns the children of this tree (the modules that are | ||
86 | // imported by this root). | ||
87 | // | ||
88 | // This will only return a non-nil value after Load is called. | ||
89 | func (t *Tree) Children() map[string]*Tree { | ||
90 | t.lock.RLock() | ||
91 | defer t.lock.RUnlock() | ||
92 | return t.children | ||
93 | } | ||
94 | |||
95 | // Loaded says whether or not this tree has been loaded or not yet. | ||
96 | func (t *Tree) Loaded() bool { | ||
97 | t.lock.RLock() | ||
98 | defer t.lock.RUnlock() | ||
99 | return t.children != nil | ||
100 | } | ||
101 | |||
102 | // Modules returns the list of modules that this tree imports. | ||
103 | // | ||
104 | // This is only the imports of _this_ level of the tree. To retrieve the | ||
105 | // full nested imports, you'll have to traverse the tree. | ||
106 | func (t *Tree) Modules() []*Module { | ||
107 | result := make([]*Module, len(t.config.Modules)) | ||
108 | for i, m := range t.config.Modules { | ||
109 | result[i] = &Module{ | ||
110 | Name: m.Name, | ||
111 | Source: m.Source, | ||
112 | } | ||
113 | } | ||
114 | |||
115 | return result | ||
116 | } | ||
117 | |||
118 | // Name returns the name of the tree. This will be "<root>" for the root | ||
119 | // tree and then the module name given for any children. | ||
120 | func (t *Tree) Name() string { | ||
121 | if t.name == "" { | ||
122 | return RootName | ||
123 | } | ||
124 | |||
125 | return t.name | ||
126 | } | ||
127 | |||
128 | // Load loads the configuration of the entire tree. | ||
129 | // | ||
130 | // The parameters are used to tell the tree where to find modules and | ||
131 | // whether it can download/update modules along the way. | ||
132 | // | ||
133 | // Calling this multiple times will reload the tree. | ||
134 | // | ||
135 | // Various semantic-like checks are made along the way of loading since | ||
136 | // module trees inherently require the configuration to be in a reasonably | ||
137 | // sane state: no circular dependencies, proper module sources, etc. A full | ||
138 | // suite of validations can be done by running Validate (after loading). | ||
139 | func (t *Tree) Load(s getter.Storage, mode GetMode) error { | ||
140 | t.lock.Lock() | ||
141 | defer t.lock.Unlock() | ||
142 | |||
143 | // Reset the children if we have any | ||
144 | t.children = nil | ||
145 | |||
146 | modules := t.Modules() | ||
147 | children := make(map[string]*Tree) | ||
148 | |||
149 | // Go through all the modules and get the directory for them. | ||
150 | for _, m := range modules { | ||
151 | if _, ok := children[m.Name]; ok { | ||
152 | return fmt.Errorf( | ||
153 | "module %s: duplicated. module names must be unique", m.Name) | ||
154 | } | ||
155 | |||
156 | // Determine the path to this child | ||
157 | path := make([]string, len(t.path), len(t.path)+1) | ||
158 | copy(path, t.path) | ||
159 | path = append(path, m.Name) | ||
160 | |||
161 | // Split out the subdir if we have one | ||
162 | source, subDir := getter.SourceDirSubdir(m.Source) | ||
163 | |||
164 | source, err := getter.Detect(source, t.config.Dir, getter.Detectors) | ||
165 | if err != nil { | ||
166 | return fmt.Errorf("module %s: %s", m.Name, err) | ||
167 | } | ||
168 | |||
169 | // Check if the detector introduced something new. | ||
170 | source, subDir2 := getter.SourceDirSubdir(source) | ||
171 | if subDir2 != "" { | ||
172 | subDir = filepath.Join(subDir2, subDir) | ||
173 | } | ||
174 | |||
175 | // Get the directory where this module is so we can load it | ||
176 | key := strings.Join(path, ".") | ||
177 | key = fmt.Sprintf("root.%s-%s", key, m.Source) | ||
178 | dir, ok, err := getStorage(s, key, source, mode) | ||
179 | if err != nil { | ||
180 | return err | ||
181 | } | ||
182 | if !ok { | ||
183 | return fmt.Errorf( | ||
184 | "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) | ||
185 | } | ||
186 | |||
187 | // If we have a subdirectory, then merge that in | ||
188 | if subDir != "" { | ||
189 | dir = filepath.Join(dir, subDir) | ||
190 | } | ||
191 | |||
192 | // Load the configurations.Dir(source) | ||
193 | children[m.Name], err = NewTreeModule(m.Name, dir) | ||
194 | if err != nil { | ||
195 | return fmt.Errorf( | ||
196 | "module %s: %s", m.Name, err) | ||
197 | } | ||
198 | |||
199 | // Set the path of this child | ||
200 | children[m.Name].path = path | ||
201 | } | ||
202 | |||
203 | // Go through all the children and load them. | ||
204 | for _, c := range children { | ||
205 | if err := c.Load(s, mode); err != nil { | ||
206 | return err | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // Set our tree up | ||
211 | t.children = children | ||
212 | |||
213 | return nil | ||
214 | } | ||
215 | |||
216 | // Path is the full path to this tree. | ||
217 | func (t *Tree) Path() []string { | ||
218 | return t.path | ||
219 | } | ||
220 | |||
221 | // String gives a nice output to describe the tree. | ||
222 | func (t *Tree) String() string { | ||
223 | var result bytes.Buffer | ||
224 | path := strings.Join(t.path, ", ") | ||
225 | if path != "" { | ||
226 | path = fmt.Sprintf(" (path: %s)", path) | ||
227 | } | ||
228 | result.WriteString(t.Name() + path + "\n") | ||
229 | |||
230 | cs := t.Children() | ||
231 | if cs == nil { | ||
232 | result.WriteString(" not loaded") | ||
233 | } else { | ||
234 | // Go through each child and get its string value, then indent it | ||
235 | // by two. | ||
236 | for _, c := range cs { | ||
237 | r := strings.NewReader(c.String()) | ||
238 | scanner := bufio.NewScanner(r) | ||
239 | for scanner.Scan() { | ||
240 | result.WriteString(" ") | ||
241 | result.WriteString(scanner.Text()) | ||
242 | result.WriteString("\n") | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | return result.String() | ||
248 | } | ||
249 | |||
250 | // Validate does semantic checks on the entire tree of configurations. | ||
251 | // | ||
252 | // This will call the respective config.Config.Validate() functions as well | ||
253 | // as verifying things such as parameters/outputs between the various modules. | ||
254 | // | ||
255 | // Load must be called prior to calling Validate or an error will be returned. | ||
256 | func (t *Tree) Validate() error { | ||
257 | if !t.Loaded() { | ||
258 | return fmt.Errorf("tree must be loaded before calling Validate") | ||
259 | } | ||
260 | |||
261 | // If something goes wrong, here is our error template | ||
262 | newErr := &treeError{Name: []string{t.Name()}} | ||
263 | |||
264 | // Terraform core does not handle root module children named "root". | ||
265 | // We plan to fix this in the future but this bug was brought up in | ||
266 | // the middle of a release and we don't want to introduce wide-sweeping | ||
267 | // changes at that time. | ||
268 | if len(t.path) == 1 && t.name == "root" { | ||
269 | return fmt.Errorf("root module cannot contain module named 'root'") | ||
270 | } | ||
271 | |||
272 | // Validate our configuration first. | ||
273 | if err := t.config.Validate(); err != nil { | ||
274 | newErr.Add(err) | ||
275 | } | ||
276 | |||
277 | // If we're the root, we do extra validation. This validation usually | ||
278 | // requires the entire tree (since children don't have parent pointers). | ||
279 | if len(t.path) == 0 { | ||
280 | if err := t.validateProviderAlias(); err != nil { | ||
281 | newErr.Add(err) | ||
282 | } | ||
283 | } | ||
284 | |||
285 | // Get the child trees | ||
286 | children := t.Children() | ||
287 | |||
288 | // Validate all our children | ||
289 | for _, c := range children { | ||
290 | err := c.Validate() | ||
291 | if err == nil { | ||
292 | continue | ||
293 | } | ||
294 | |||
295 | verr, ok := err.(*treeError) | ||
296 | if !ok { | ||
297 | // Unknown error, just return... | ||
298 | return err | ||
299 | } | ||
300 | |||
301 | // Append ourselves to the error and then return | ||
302 | verr.Name = append(verr.Name, t.Name()) | ||
303 | newErr.AddChild(verr) | ||
304 | } | ||
305 | |||
306 | // Go over all the modules and verify that any parameters are valid | ||
307 | // variables into the module in question. | ||
308 | for _, m := range t.config.Modules { | ||
309 | tree, ok := children[m.Name] | ||
310 | if !ok { | ||
311 | // This should never happen because Load watches us | ||
312 | panic("module not found in children: " + m.Name) | ||
313 | } | ||
314 | |||
315 | // Build the variables that the module defines | ||
316 | requiredMap := make(map[string]struct{}) | ||
317 | varMap := make(map[string]struct{}) | ||
318 | for _, v := range tree.config.Variables { | ||
319 | varMap[v.Name] = struct{}{} | ||
320 | |||
321 | if v.Required() { | ||
322 | requiredMap[v.Name] = struct{}{} | ||
323 | } | ||
324 | } | ||
325 | |||
326 | // Compare to the keys in our raw config for the module | ||
327 | for k, _ := range m.RawConfig.Raw { | ||
328 | if _, ok := varMap[k]; !ok { | ||
329 | newErr.Add(fmt.Errorf( | ||
330 | "module %s: %s is not a valid parameter", | ||
331 | m.Name, k)) | ||
332 | } | ||
333 | |||
334 | // Remove the required | ||
335 | delete(requiredMap, k) | ||
336 | } | ||
337 | |||
338 | // If we have any required left over, they aren't set. | ||
339 | for k, _ := range requiredMap { | ||
340 | newErr.Add(fmt.Errorf( | ||
341 | "module %s: required variable %q not set", | ||
342 | m.Name, k)) | ||
343 | } | ||
344 | } | ||
345 | |||
346 | // Go over all the variables used and make sure that any module | ||
347 | // variables represent outputs properly. | ||
348 | for source, vs := range t.config.InterpolatedVariables() { | ||
349 | for _, v := range vs { | ||
350 | mv, ok := v.(*config.ModuleVariable) | ||
351 | if !ok { | ||
352 | continue | ||
353 | } | ||
354 | |||
355 | tree, ok := children[mv.Name] | ||
356 | if !ok { | ||
357 | newErr.Add(fmt.Errorf( | ||
358 | "%s: undefined module referenced %s", | ||
359 | source, mv.Name)) | ||
360 | continue | ||
361 | } | ||
362 | |||
363 | found := false | ||
364 | for _, o := range tree.config.Outputs { | ||
365 | if o.Name == mv.Field { | ||
366 | found = true | ||
367 | break | ||
368 | } | ||
369 | } | ||
370 | if !found { | ||
371 | newErr.Add(fmt.Errorf( | ||
372 | "%s: %s is not a valid output for module %s", | ||
373 | source, mv.Field, mv.Name)) | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | return newErr.ErrOrNil() | ||
379 | } | ||
380 | |||
381 | // treeError is an error use by Tree.Validate to accumulates all | ||
382 | // validation errors. | ||
383 | type treeError struct { | ||
384 | Name []string | ||
385 | Errs []error | ||
386 | Children []*treeError | ||
387 | } | ||
388 | |||
389 | func (e *treeError) Add(err error) { | ||
390 | e.Errs = append(e.Errs, err) | ||
391 | } | ||
392 | |||
393 | func (e *treeError) AddChild(err *treeError) { | ||
394 | e.Children = append(e.Children, err) | ||
395 | } | ||
396 | |||
397 | func (e *treeError) ErrOrNil() error { | ||
398 | if len(e.Errs) > 0 || len(e.Children) > 0 { | ||
399 | return e | ||
400 | } | ||
401 | return nil | ||
402 | } | ||
403 | |||
404 | func (e *treeError) Error() string { | ||
405 | name := strings.Join(e.Name, ".") | ||
406 | var out bytes.Buffer | ||
407 | fmt.Fprintf(&out, "module %s: ", name) | ||
408 | |||
409 | if len(e.Errs) == 1 { | ||
410 | // single like error | ||
411 | out.WriteString(e.Errs[0].Error()) | ||
412 | } else { | ||
413 | // multi-line error | ||
414 | for _, err := range e.Errs { | ||
415 | fmt.Fprintf(&out, "\n %s", err) | ||
416 | } | ||
417 | } | ||
418 | |||
419 | if len(e.Children) > 0 { | ||
420 | // start the next error on a new line | ||
421 | out.WriteString("\n ") | ||
422 | } | ||
423 | for _, child := range e.Children { | ||
424 | out.WriteString(child.Error()) | ||
425 | } | ||
426 | |||
427 | return out.String() | ||
428 | } | ||