github.com/paybyphone/terraform@v0.9.5-0.20170613192930-9706042ddd51/config/module/tree.go (about)

     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  // 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.
    98  //
    99  // Parents will be visited before their children but otherwise the order is
   100  // not defined.
   101  func (t *Tree) DeepEach(cb func(*Tree)) {
   102  	t.lock.RLock()
   103  	defer t.lock.RUnlock()
   104  	t.deepEach(cb)
   105  }
   106  
   107  func (t *Tree) deepEach(cb func(*Tree)) {
   108  	cb(t)
   109  	for _, c := range t.children {
   110  		c.deepEach(cb)
   111  	}
   112  }
   113  
   114  // Loaded says whether or not this tree has been loaded or not yet.
   115  func (t *Tree) Loaded() bool {
   116  	t.lock.RLock()
   117  	defer t.lock.RUnlock()
   118  	return t.children != nil
   119  }
   120  
   121  // Modules returns the list of modules that this tree imports.
   122  //
   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 {
   128  		result[i] = &Module{
   129  			Name:   m.Name,
   130  			Source: m.Source,
   131  		}
   132  	}
   133  
   134  	return result
   135  }
   136  
   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 {
   140  	if t.name == "" {
   141  		return RootName
   142  	}
   143  
   144  	return t.name
   145  }
   146  
   147  // Load loads the configuration of the entire tree.
   148  //
   149  // The parameters are used to tell the tree where to find modules and
   150  // whether it can download/update modules along the way.
   151  //
   152  // Calling this multiple times will reload the tree.
   153  //
   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 {
   159  	t.lock.Lock()
   160  	defer t.lock.Unlock()
   161  
   162  	// Reset the children if we have any
   163  	t.children = nil
   164  
   165  	modules := t.Modules()
   166  	children := make(map[string]*Tree)
   167  
   168  	// Go through all the modules and get the directory for them.
   169  	for _, m := range modules {
   170  		if _, ok := children[m.Name]; ok {
   171  			return fmt.Errorf(
   172  				"module %s: duplicated. module names must be unique", m.Name)
   173  		}
   174  
   175  		// Determine the path to this child
   176  		path := make([]string, len(t.path), len(t.path)+1)
   177  		copy(path, t.path)
   178  		path = append(path, m.Name)
   179  
   180  		// Split out the subdir if we have one
   181  		source, subDir := getter.SourceDirSubdir(m.Source)
   182  
   183  		source, err := getter.Detect(source, t.config.Dir, getter.Detectors)
   184  		if err != nil {
   185  			return fmt.Errorf("module %s: %s", m.Name, err)
   186  		}
   187  
   188  		// Check if the detector introduced something new.
   189  		source, subDir2 := getter.SourceDirSubdir(source)
   190  		if subDir2 != "" {
   191  			subDir = filepath.Join(subDir2, subDir)
   192  		}
   193  
   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)
   198  		if err != nil {
   199  			return err
   200  		}
   201  		if !ok {
   202  			return fmt.Errorf(
   203  				"module %s: not found, may need to be downloaded using 'terraform get'", m.Name)
   204  		}
   205  
   206  		// If we have a subdirectory, then merge that in
   207  		if subDir != "" {
   208  			dir = filepath.Join(dir, subDir)
   209  		}
   210  
   211  		// Load the configurations.Dir(source)
   212  		children[m.Name], err = NewTreeModule(m.Name, dir)
   213  		if err != nil {
   214  			return fmt.Errorf(
   215  				"module %s: %s", m.Name, err)
   216  		}
   217  
   218  		// Set the path of this child
   219  		children[m.Name].path = path
   220  	}
   221  
   222  	// Go through all the children and load them.
   223  	for _, c := range children {
   224  		if err := c.Load(s, mode); err != nil {
   225  			return err
   226  		}
   227  	}
   228  
   229  	// Set our tree up
   230  	t.children = children
   231  
   232  	return nil
   233  }
   234  
   235  // Path is the full path to this tree.
   236  func (t *Tree) Path() []string {
   237  	return t.path
   238  }
   239  
   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, ", ")
   244  	if path != "" {
   245  		path = fmt.Sprintf(" (path: %s)", path)
   246  	}
   247  	result.WriteString(t.Name() + path + "\n")
   248  
   249  	cs := t.Children()
   250  	if cs == nil {
   251  		result.WriteString("  not loaded")
   252  	} else {
   253  		// Go through each child and get its string value, then indent it
   254  		// by two.
   255  		for _, c := range cs {
   256  			r := strings.NewReader(c.String())
   257  			scanner := bufio.NewScanner(r)
   258  			for scanner.Scan() {
   259  				result.WriteString("  ")
   260  				result.WriteString(scanner.Text())
   261  				result.WriteString("\n")
   262  			}
   263  		}
   264  	}
   265  
   266  	return result.String()
   267  }
   268  
   269  // Validate does semantic checks on the entire tree of configurations.
   270  //
   271  // This will call the respective config.Config.Validate() functions as well
   272  // as verifying things such as parameters/outputs between the various modules.
   273  //
   274  // Load must be called prior to calling Validate or an error will be returned.
   275  func (t *Tree) Validate() error {
   276  	if !t.Loaded() {
   277  		return fmt.Errorf("tree must be loaded before calling Validate")
   278  	}
   279  
   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".
   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'")
   289  	}
   290  
   291  	// Validate our configuration first.
   292  	if err := t.config.Validate(); err != nil {
   293  		newErr.Add(err)
   294  	}
   295  
   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 {
   300  			newErr.Add(err)
   301  		}
   302  	}
   303  
   304  	// Get the child trees
   305  	children := t.Children()
   306  
   307  	// Validate all our children
   308  	for _, c := range children {
   309  		err := c.Validate()
   310  		if err == nil {
   311  			continue
   312  		}
   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  	}
   324  
   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]
   329  		if !ok {
   330  			// This should never happen because Load watches us
   331  			panic("module not found in children: " + m.Name)
   332  		}
   333  
   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{}{}
   339  
   340  			if v.Required() {
   341  				requiredMap[v.Name] = struct{}{}
   342  			}
   343  		}
   344  
   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",
   350  					m.Name, k))
   351  			}
   352  
   353  			// Remove the required
   354  			delete(requiredMap, k)
   355  		}
   356  
   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",
   361  				m.Name, k))
   362  		}
   363  	}
   364  
   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)
   370  			if !ok {
   371  				continue
   372  			}
   373  
   374  			tree, ok := children[mv.Name]
   375  			if !ok {
   376  				newErr.Add(fmt.Errorf(
   377  					"%s: undefined module referenced %s",
   378  					source, mv.Name))
   379  				continue
   380  			}
   381  
   382  			found := false
   383  			for _, o := range tree.config.Outputs {
   384  				if o.Name == mv.Field {
   385  					found = true
   386  					break
   387  				}
   388  			}
   389  			if !found {
   390  				newErr.Add(fmt.Errorf(
   391  					"%s: %s is not a valid output for module %s",
   392  					source, mv.Field, mv.Name))
   393  			}
   394  		}
   395  	}
   396  
   397  	return newErr.ErrOrNil()
   398  }
   399  
   400  // treeError is an error use by Tree.Validate to accumulates all
   401  // validation errors.
   402  type treeError struct {
   403  	Name     []string
   404  	Errs     []error
   405  	Children []*treeError
   406  }
   407  
   408  func (e *treeError) Add(err error) {
   409  	e.Errs = append(e.Errs, err)
   410  }
   411  
   412  func (e *treeError) AddChild(err *treeError) {
   413  	e.Children = append(e.Children, err)
   414  }
   415  
   416  func (e *treeError) ErrOrNil() error {
   417  	if len(e.Errs) > 0 || len(e.Children) > 0 {
   418  		return e
   419  	}
   420  	return nil
   421  }
   422  
   423  func (e *treeError) Error() string {
   424  	name := strings.Join(e.Name, ".")
   425  	var out bytes.Buffer
   426  	fmt.Fprintf(&out, "module %s: ", name)
   427  
   428  	if len(e.Errs) == 1 {
   429  		// single like error
   430  		out.WriteString(e.Errs[0].Error())
   431  	} else {
   432  		// multi-line error
   433  		for _, err := range e.Errs {
   434  			fmt.Fprintf(&out, "\n    %s", err)
   435  		}
   436  	}
   437  
   438  	if len(e.Children) > 0 {
   439  		// start the next error on a new line
   440  		out.WriteString("\n  ")
   441  	}
   442  	for _, child := range e.Children {
   443  		out.WriteString(child.Error())
   444  	}
   445  
   446  	return out.String()
   447  }