11 "github.com/hashicorp/go-getter"
12 "github.com/hashicorp/terraform/config"
15 // RootName is the name of the root tree.
16 const RootName = "root"
18 // Tree represents the module import tree of configurations.
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.
26 children map[string]*Tree
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}
36 // NewEmptyTree returns a new tree that is empty (contains no configuration).
37 func NewEmptyTree() *Tree {
38 t := &Tree{config: &config.Config{}}
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 {
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
53 func NewTreeModule(name, dir string) (*Tree, error) {
54 c, err := config.LoadDir(dir)
59 return NewTree(name, c), nil
62 // Config returns the configuration for this module.
63 func (t *Tree) Config() *config.Config {
67 // Child returns the child with the given path (by name).
68 func (t *Tree) Child(path []string) *Tree {
77 c := t.Children()[path[0]]
82 return c.Child(path[1:])
85 // Children returns the children of this tree (the modules that are
86 // imported by this root).
88 // This will only return a non-nil value after Load is called.
89 func (t *Tree) Children() map[string]*Tree {
91 defer t.lock.RUnlock()
95 // DeepEach calls the provided callback for the receiver and then all of
96 // its descendents in the tree, allowing an operation to be performed on
97 // all modules in the tree.
99 // Parents will be visited before their children but otherwise the order is
101 func (t *Tree) DeepEach(cb func(*Tree)) {
103 defer t.lock.RUnlock()
107 func (t *Tree) deepEach(cb func(*Tree)) {
109 for _, c := range t.children {
114 // Loaded says whether or not this tree has been loaded or not yet.
115 func (t *Tree) Loaded() bool {
117 defer t.lock.RUnlock()
118 return t.children != nil
121 // Modules returns the list of modules that this tree imports.
123 // This is only the imports of _this_ level of the tree. To retrieve the
124 // full nested imports, you'll have to traverse the tree.
125 func (t *Tree) Modules() []*Module {
126 result := make([]*Module, len(t.config.Modules))
127 for i, m := range t.config.Modules {
137 // Name returns the name of the tree. This will be "<root>" for the root
138 // tree and then the module name given for any children.
139 func (t *Tree) Name() string {
147 // Load loads the configuration of the entire tree.
149 // The parameters are used to tell the tree where to find modules and
150 // whether it can download/update modules along the way.
152 // Calling this multiple times will reload the tree.
154 // Various semantic-like checks are made along the way of loading since
155 // module trees inherently require the configuration to be in a reasonably
156 // sane state: no circular dependencies, proper module sources, etc. A full
157 // suite of validations can be done by running Validate (after loading).
158 func (t *Tree) Load(s getter.Storage, mode GetMode) error {
160 defer t.lock.Unlock()
162 // Reset the children if we have any
165 modules := t.Modules()
166 children := make(map[string]*Tree)
168 // Go through all the modules and get the directory for them.
169 for _, m := range modules {
170 if _, ok := children[m.Name]; ok {
172 "module %s: duplicated. module names must be unique", m.Name)
175 // Determine the path to this child
176 path := make([]string, len(t.path), len(t.path)+1)
178 path = append(path, m.Name)
180 // Split out the subdir if we have one
181 source, subDir := getter.SourceDirSubdir(m.Source)
183 source, err := getter.Detect(source, t.config.Dir, getter.Detectors)
185 return fmt.Errorf("module %s: %s", m.Name, err)
188 // Check if the detector introduced something new.
189 source, subDir2 := getter.SourceDirSubdir(source)
191 subDir = filepath.Join(subDir2, subDir)
194 // Get the directory where this module is so we can load it
195 key := strings.Join(path, ".")
196 key = fmt.Sprintf("root.%s-%s", key, m.Source)
197 dir, ok, err := getStorage(s, key, source, mode)
203 "module %s: not found, may need to be downloaded using 'terraform get'", m.Name)
206 // If we have a subdirectory, then merge that in
208 dir = filepath.Join(dir, subDir)
211 // Load the configurations.Dir(source)
212 children[m.Name], err = NewTreeModule(m.Name, dir)
215 "module %s: %s", m.Name, err)
218 // Set the path of this child
219 children[m.Name].path = path
222 // Go through all the children and load them.
223 for _, c := range children {
224 if err := c.Load(s, mode); err != nil {
230 t.children = children
235 // Path is the full path to this tree.
236 func (t *Tree) Path() []string {
240 // String gives a nice output to describe the tree.
241 func (t *Tree) String() string {
242 var result bytes.Buffer
243 path := strings.Join(t.path, ", ")
245 path = fmt.Sprintf(" (path: %s)", path)
247 result.WriteString(t.Name() + path + "\n")
251 result.WriteString(" not loaded")
253 // Go through each child and get its string value, then indent it
255 for _, c := range cs {
256 r := strings.NewReader(c.String())
257 scanner := bufio.NewScanner(r)
259 result.WriteString(" ")
260 result.WriteString(scanner.Text())
261 result.WriteString("\n")
266 return result.String()
269 // Validate does semantic checks on the entire tree of configurations.
271 // This will call the respective config.Config.Validate() functions as well
272 // as verifying things such as parameters/outputs between the various modules.
274 // Load must be called prior to calling Validate or an error will be returned.
275 func (t *Tree) Validate() error {
277 return fmt.Errorf("tree must be loaded before calling Validate")
280 // If something goes wrong, here is our error template
281 newErr := &treeError{Name: []string{t.Name()}}
283 // 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
285 // the middle of a release and we don't want to introduce wide-sweeping
286 // changes at that time.
287 if len(t.path) == 1 && t.name == "root" {
288 return fmt.Errorf("root module cannot contain module named 'root'")
291 // Validate our configuration first.
292 if err := t.config.Validate(); err != nil {
296 // If we're the root, we do extra validation. This validation usually
297 // requires the entire tree (since children don't have parent pointers).
298 if len(t.path) == 0 {
299 if err := t.validateProviderAlias(); err != nil {
304 // Get the child trees
305 children := t.Children()
307 // Validate all our children
308 for _, c := range children {
314 verr, ok := err.(*treeError)
316 // Unknown error, just return...
320 // Append ourselves to the error and then return
321 verr.Name = append(verr.Name, t.Name())
322 newErr.AddChild(verr)
325 // Go over all the modules and verify that any parameters are valid
326 // variables into the module in question.
327 for _, m := range t.config.Modules {
328 tree, ok := children[m.Name]
330 // This should never happen because Load watches us
331 panic("module not found in children: " + m.Name)
334 // Build the variables that the module defines
335 requiredMap := make(map[string]struct{})
336 varMap := make(map[string]struct{})
337 for _, v := range tree.config.Variables {
338 varMap[v.Name] = struct{}{}
341 requiredMap[v.Name] = struct{}{}
345 // Compare to the keys in our raw config for the module
346 for k, _ := range m.RawConfig.Raw {
347 if _, ok := varMap[k]; !ok {
348 newErr.Add(fmt.Errorf(
349 "module %s: %s is not a valid parameter",
353 // Remove the required
354 delete(requiredMap, k)
357 // If we have any required left over, they aren't set.
358 for k, _ := range requiredMap {
359 newErr.Add(fmt.Errorf(
360 "module %s: required variable %q not set",
365 // Go over all the variables used and make sure that any module
366 // variables represent outputs properly.
367 for source, vs := range t.config.InterpolatedVariables() {
368 for _, v := range vs {
369 mv, ok := v.(*config.ModuleVariable)
374 tree, ok := children[mv.Name]
376 newErr.Add(fmt.Errorf(
377 "%s: undefined module referenced %s",
383 for _, o := range tree.config.Outputs {
384 if o.Name == mv.Field {
390 newErr.Add(fmt.Errorf(
391 "%s: %s is not a valid output for module %s",
392 source, mv.Field, mv.Name))
397 return newErr.ErrOrNil()
400 // treeError is an error use by Tree.Validate to accumulates all
401 // validation errors.
402 type treeError struct {
405 Children []*treeError
408 func (e *treeError) Add(err error) {
409 e.Errs = append(e.Errs, err)
412 func (e *treeError) AddChild(err *treeError) {
413 e.Children = append(e.Children, err)
416 func (e *treeError) ErrOrNil() error {
417 if len(e.Errs) > 0 || len(e.Children) > 0 {
423 func (e *treeError) Error() string {
424 name := strings.Join(e.Name, ".")
426 fmt.Fprintf(&out, "module %s: ", name)
428 if len(e.Errs) == 1 {
430 out.WriteString(e.Errs[0].Error())
433 for _, err := range e.Errs {
434 fmt.Fprintf(&out, "\n %s", err)
438 if len(e.Children) > 0 {
439 // start the next error on a new line
440 out.WriteString("\n ")
442 for _, child := range e.Children {
443 out.WriteString(child.Error())